From d3764dc34fe6bc42b8c83b0fbed19a41ddfbad3b Mon Sep 17 00:00:00 2001 From: swit-jim Date: Mon, 11 Mar 2024 20:58:00 +0900 Subject: [PATCH 1/7] =?UTF-8?q?[feat/group=5Fselect]:=20=EC=86=8C=EC=86=8D?= =?UTF-8?q?=EB=90=9C=20Group=20=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gdsc/data/datasource/GroupDataSource.kt | 8 +++ .../data/datasource/GroupDataSourceImpl.kt | 21 ++++++ .../main/java/org/gdsc/data/di/ApiModule.kt | 7 ++ .../main/java/org/gdsc/data/di/GroupModule.kt | 25 +++++++ .../java/org/gdsc/data/model/GroupResponse.kt | 14 ++++ .../java/org/gdsc/data/network/GroupAPI.kt | 11 +++ .../data/repository/GroupRepositoryImpl.kt | 15 ++++ .../domain/model/response/GroupResponse.kt | 13 ++++ .../gdsc/domain/repository/GroupRepository.kt | 7 ++ .../gdsc/domain/usecase/GetMyGroupUseCase.kt | 10 +++ .../gdsc/presentation/view/WebViewActivity.kt | 39 ++++++++--- .../view/group/MyGroupFragment.kt | 6 +- .../presentation/view/home/HomeFragment.kt | 53 +++++++++++++- .../presentation/view/home/HomeViewModel.kt | 32 ++++++--- .../src/main/res/drawable/ic_jmt_new_logo.xml | 17 +++++ .../res/layout/content_sheet_empty_group.xml | 69 +++++++++++++++++++ .../src/main/res/layout/fragment_home.xml | 42 +++++++++++ 17 files changed, 362 insertions(+), 27 deletions(-) create mode 100644 data/src/main/java/org/gdsc/data/datasource/GroupDataSource.kt create mode 100644 data/src/main/java/org/gdsc/data/datasource/GroupDataSourceImpl.kt create mode 100644 data/src/main/java/org/gdsc/data/di/GroupModule.kt create mode 100644 data/src/main/java/org/gdsc/data/model/GroupResponse.kt create mode 100644 data/src/main/java/org/gdsc/data/network/GroupAPI.kt create mode 100644 data/src/main/java/org/gdsc/data/repository/GroupRepositoryImpl.kt create mode 100644 domain/src/main/java/org/gdsc/domain/model/response/GroupResponse.kt create mode 100644 domain/src/main/java/org/gdsc/domain/repository/GroupRepository.kt create mode 100644 domain/src/main/java/org/gdsc/domain/usecase/GetMyGroupUseCase.kt create mode 100644 presentation/src/main/res/drawable/ic_jmt_new_logo.xml create mode 100644 presentation/src/main/res/layout/content_sheet_empty_group.xml diff --git a/data/src/main/java/org/gdsc/data/datasource/GroupDataSource.kt b/data/src/main/java/org/gdsc/data/datasource/GroupDataSource.kt new file mode 100644 index 00000000..060b6015 --- /dev/null +++ b/data/src/main/java/org/gdsc/data/datasource/GroupDataSource.kt @@ -0,0 +1,8 @@ +package org.gdsc.data.datasource + +import org.gdsc.domain.model.response.GroupResponse + + +interface GroupDataSource { + suspend fun getMyGroups(): List +} \ 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 new file mode 100644 index 00000000..bd93cba9 --- /dev/null +++ b/data/src/main/java/org/gdsc/data/datasource/GroupDataSourceImpl.kt @@ -0,0 +1,21 @@ +package org.gdsc.data.datasource + +import android.util.Log +import org.gdsc.data.network.GroupAPI +import org.gdsc.domain.model.response.GroupResponse +import javax.inject.Inject + +class GroupDataSourceImpl @Inject constructor( + private val groupAPI: GroupAPI, +): GroupDataSource { + override suspend fun getMyGroups(): List { + runCatching { + groupAPI.getMyGroups() + }.onSuccess { + return it.data + }.onFailure { + return emptyList() + } + return emptyList() + } +} \ No newline at end of file diff --git a/data/src/main/java/org/gdsc/data/di/ApiModule.kt b/data/src/main/java/org/gdsc/data/di/ApiModule.kt index 598998be..2395def9 100644 --- a/data/src/main/java/org/gdsc/data/di/ApiModule.kt +++ b/data/src/main/java/org/gdsc/data/di/ApiModule.kt @@ -4,6 +4,7 @@ import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import org.gdsc.data.network.GroupAPI import org.gdsc.data.network.LoginAPI import org.gdsc.data.network.RestaurantAPI import org.gdsc.data.network.TokenAPI @@ -39,4 +40,10 @@ class ApiModule { return retrofit.create(TokenAPI::class.java) } + @Provides + @Singleton + fun provideGroupApi(@AuthClient retrofit: Retrofit): GroupAPI { + return retrofit.create(GroupAPI::class.java) + } + } \ No newline at end of file diff --git a/data/src/main/java/org/gdsc/data/di/GroupModule.kt b/data/src/main/java/org/gdsc/data/di/GroupModule.kt new file mode 100644 index 00000000..a4c9962f --- /dev/null +++ b/data/src/main/java/org/gdsc/data/di/GroupModule.kt @@ -0,0 +1,25 @@ +package org.gdsc.data.di + +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import org.gdsc.data.datasource.GroupDataSource +import org.gdsc.data.datasource.GroupDataSourceImpl +import org.gdsc.data.repository.GroupRepositoryImpl +import org.gdsc.domain.repository.GroupRepository +import javax.inject.Singleton + + +@Module +@InstallIn(SingletonComponent::class) +abstract class GroupModule { + + @Singleton + @Binds + abstract fun bindGroupDataSource(groupDataSourceImpl: GroupDataSourceImpl): GroupDataSource + + @Singleton + @Binds + abstract fun bindGroupRepository(groupRepositoryImpl: GroupRepositoryImpl): GroupRepository +} \ No newline at end of file diff --git a/data/src/main/java/org/gdsc/data/model/GroupResponse.kt b/data/src/main/java/org/gdsc/data/model/GroupResponse.kt new file mode 100644 index 00000000..c714d8c0 --- /dev/null +++ b/data/src/main/java/org/gdsc/data/model/GroupResponse.kt @@ -0,0 +1,14 @@ +package org.gdsc.data.model + +import com.google.gson.annotations.SerializedName + +data class GroupResponse( + @SerializedName("groupId") val groupId: Int, + @SerializedName("groupIntroduce") val groupIntroduce: String, + @SerializedName("groupProfileImageUrl") val groupProfileImageUrl: String, + @SerializedName("groupBackgroundImageUrl") val groupBackgroundImageUrl: String, + @SerializedName("memberCnt") val memberCnt: Int, + @SerializedName("restaurantCnt") val restaurantCnt: Int, + @SerializedName("privateGroup") val privateGroup: Boolean, + @SerializedName("isSelected") val isSelected: Boolean, +) diff --git a/data/src/main/java/org/gdsc/data/network/GroupAPI.kt b/data/src/main/java/org/gdsc/data/network/GroupAPI.kt new file mode 100644 index 00000000..06417a98 --- /dev/null +++ b/data/src/main/java/org/gdsc/data/network/GroupAPI.kt @@ -0,0 +1,11 @@ +package org.gdsc.data.network + +import org.gdsc.data.model.Response +import org.gdsc.domain.model.response.GroupResponse +import retrofit2.http.GET + +interface GroupAPI { + + @GET("api/v1/group/my") + suspend fun getMyGroups(): Response> +} \ 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 new file mode 100644 index 00000000..630e0abd --- /dev/null +++ b/data/src/main/java/org/gdsc/data/repository/GroupRepositoryImpl.kt @@ -0,0 +1,15 @@ +package org.gdsc.data.repository + +import org.gdsc.data.datasource.GroupDataSource +import org.gdsc.domain.model.response.GroupResponse +import org.gdsc.domain.repository.GroupRepository +import javax.inject.Inject + +class GroupRepositoryImpl @Inject constructor( + private val groupDataSource: GroupDataSource +): GroupRepository { + override suspend fun getMyGroups(): List { + return groupDataSource.getMyGroups() + } + +} \ No newline at end of file diff --git a/domain/src/main/java/org/gdsc/domain/model/response/GroupResponse.kt b/domain/src/main/java/org/gdsc/domain/model/response/GroupResponse.kt new file mode 100644 index 00000000..2740ed4a --- /dev/null +++ b/domain/src/main/java/org/gdsc/domain/model/response/GroupResponse.kt @@ -0,0 +1,13 @@ +package org.gdsc.domain.model.response + +data class GroupResponse( + val groupId: Int, + val groupName: String, + val groupIntroduce: String, + val groupProfileImageUrl: String, + val groupBackgroundImageUrl: String, + val memberCnt: Int, + val restaurantCnt: Int, + val privateGroup: Boolean, + val isSelected: Boolean, +) \ 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 new file mode 100644 index 00000000..54d51864 --- /dev/null +++ b/domain/src/main/java/org/gdsc/domain/repository/GroupRepository.kt @@ -0,0 +1,7 @@ +package org.gdsc.domain.repository + +import org.gdsc.domain.model.response.GroupResponse + +interface GroupRepository { + suspend fun getMyGroups(): List +} \ No newline at end of file diff --git a/domain/src/main/java/org/gdsc/domain/usecase/GetMyGroupUseCase.kt b/domain/src/main/java/org/gdsc/domain/usecase/GetMyGroupUseCase.kt new file mode 100644 index 00000000..ce21dbb1 --- /dev/null +++ b/domain/src/main/java/org/gdsc/domain/usecase/GetMyGroupUseCase.kt @@ -0,0 +1,10 @@ +package org.gdsc.domain.usecase + +import org.gdsc.domain.repository.GroupRepository +import javax.inject.Inject + +class GetMyGroupUseCase @Inject constructor( + private val groupRepository: GroupRepository +){ + suspend operator fun invoke() = groupRepository.getMyGroups() +} \ No newline at end of file diff --git a/presentation/src/main/java/org/gdsc/presentation/view/WebViewActivity.kt b/presentation/src/main/java/org/gdsc/presentation/view/WebViewActivity.kt index 4e1f98d0..c94f8c0b 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/WebViewActivity.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/WebViewActivity.kt @@ -15,6 +15,7 @@ import android.webkit.WebViewClient import androidx.activity.addCallback import androidx.appcompat.app.AppCompatActivity import org.gdsc.presentation.databinding.ActivityWebViewBinding +import org.gdsc.presentation.utils.repeatWhenUiStarted class WebViewActivity : AppCompatActivity() { @@ -25,24 +26,40 @@ class WebViewActivity : AppCompatActivity() { binding = ActivityWebViewBinding.inflate(layoutInflater) setContentView(binding.root) - onBackPressedDispatcher.addCallback { - if(webView.canGoBack()) { - webView.goBack() - } else { - // TODO(): 메인으로 이동 등, 페이지 이동 기능 추가 - finish() - } - } +// webViewInit() webView = binding.webView - webViewInit() + + setWebViewBackPress() intent.extras?.let { - binding.webView.loadUrl(it.getString("url")!!) + binding.webView.apply { + + repeatWhenUiStarted { + loadUrl(it.getString("url") ?: WEB_BASE_URL) + } + + settings.javaScriptEnabled = true + settings.domStorageEnabled = true + webViewClient = WebViewClient() + + addJavascriptInterface(WebAppInterface(context), "webviewBridge") + } + } // val actionBar: ActionBar? = supportActionBar // actionBar!!.hide() } + private fun setWebViewBackPress() { + this.onBackPressedDispatcher.addCallback(this) { + if (binding.webView.canGoBack()) { + binding.webView.goBack() + } else { + finish() + } + } + } + fun webViewInit() { val webSettings: WebSettings = webView.getSettings() webSettings.javaScriptEnabled = true // allow the js @@ -51,6 +68,6 @@ class WebViewActivity : AppCompatActivity() { requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_FULL_USER webView.webViewClient = WebViewClient() - webView.addJavascriptInterface(WebAppInterface(this), "Android") + webView.addJavascriptInterface(WebAppInterface(this), "webviewBridge") } } diff --git a/presentation/src/main/java/org/gdsc/presentation/view/group/MyGroupFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/group/MyGroupFragment.kt index 33707ac8..c2602793 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/group/MyGroupFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/group/MyGroupFragment.kt @@ -39,11 +39,7 @@ class MyGroupFragment: Fragment() { binding.webView.apply { repeatWhenUiStarted { - loadUrl( - WEB_BASE_URL, - hashMapOf().apply { - put("token", specificWebViewViewModel.getAccessToken()) - }) + loadUrl(WEB_BASE_URL) } settings.javaScriptEnabled = true diff --git a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt index 56c03a90..5cf989c6 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt @@ -1,8 +1,8 @@ package org.gdsc.presentation.view.home +import android.content.Intent import android.graphics.PointF import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -35,9 +35,11 @@ import org.gdsc.domain.model.RegisteredRestaurant import org.gdsc.presentation.R import org.gdsc.presentation.base.BaseViewHolder import org.gdsc.presentation.base.ViewHolderBindListener +import org.gdsc.presentation.databinding.ContentSheetEmptyGroupBinding import org.gdsc.presentation.databinding.FragmentHomeBinding import org.gdsc.presentation.utils.repeatWhenUiStarted -import org.gdsc.presentation.utils.toDp +import org.gdsc.presentation.view.WebViewActivity +import org.gdsc.presentation.view.custom.BottomSheetDialog import org.gdsc.presentation.view.custom.JmtSpinner @@ -68,6 +70,7 @@ class HomeFragment : Fragment(), ViewHolderBindListener { ): View { _binding = FragmentHomeBinding.inflate(inflater, container, false) + setGroup() setMap(savedInstanceState) observeState() @@ -84,7 +87,7 @@ class HomeFragment : Fragment(), ViewHolderBindListener { mapMarkerAdapter ) } - setRecyclerView() +// setRecyclerView() return binding.root } @@ -177,6 +180,49 @@ class HomeFragment : Fragment(), ViewHolderBindListener { } } + private fun setGroup() { + repeatWhenUiStarted { + viewModel.getMyGroup().let { groupList -> + if (groupList.isEmpty()) { + BottomSheetDialog(requireContext()) + .bindBuilder( + ContentSheetEmptyGroupBinding.inflate(LayoutInflater.from(requireContext())) + ) { dialog -> + with(dialog) { + binding.groupHeader.isVisible = false + dialog.setCancelable(false) + dialog.behavior.isDraggable = false + createGroupButton.setOnClickListener { + startActivity( + Intent(requireContext(), WebViewActivity::class.java) + ) + } + show() + } + } + } else { + binding.groupHeader.isVisible = true + groupList.forEach { + if (it.isSelected) { + binding.groupName.text = it.groupName + if (it.restaurantCnt == 0) { + + binding.recyclerView.isVisible = false + binding.registGroup.isVisible = true + } + return@forEach + } + } + + // 선택 된 그룹이 없는 경우 + if (groupList.isNotEmpty()) { + binding.groupName.text = groupList[0].groupName + } + } + } + } + } + private fun setMap(savedInstanceState: Bundle?) { mapView = binding.mapView mapView.onCreate(savedInstanceState) @@ -255,6 +301,7 @@ class HomeFragment : Fragment(), ViewHolderBindListener { repeatWhenUiStarted { viewModel.setSortType(SortType.DISTANCE) } + } override fun onDestroyView() { diff --git a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt index 6562d183..0f7848ca 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt @@ -23,6 +23,8 @@ import org.gdsc.domain.SortType import org.gdsc.domain.model.Location import org.gdsc.domain.model.PagingResult import org.gdsc.domain.model.RegisteredRestaurant +import org.gdsc.domain.model.response.GroupResponse +import org.gdsc.domain.usecase.GetMyGroupUseCase import org.gdsc.domain.usecase.GetRestaurantsByMapUseCase import org.gdsc.domain.usecase.token.GetAccessTokenUseCase import org.gdsc.presentation.JmtLocationManager @@ -31,7 +33,8 @@ import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( private val locationManager: JmtLocationManager, - private val getRestaurantsByMapUseCase: GetRestaurantsByMapUseCase + private val getRestaurantsByMapUseCase: GetRestaurantsByMapUseCase, + private val getMyGroupUseCase: GetMyGroupUseCase, ) : ViewModel() { suspend fun getCurrentLocation() = locationManager.getCurrentLocation() @@ -62,13 +65,6 @@ class HomeViewModel @Inject constructor( val drinkPossibilityState: StateFlow get() = _drinkPossibilityState - private var _markerState = MutableStateFlow(listOf()) - val markerState: StateFlow> - get() = _markerState - - fun setMarkerState(value: List) { - _markerState.value = value - } fun setUserLocation(userLocation: Location) { _userLocationState.value = userLocation @@ -113,4 +109,24 @@ class HomeViewModel @Inject constructor( .flatMapLatest { it } }.cachedIn(viewModelScope) } + + @OptIn(ExperimentalCoroutinesApi::class) + suspend fun registeredPagingDataByGroup(): Flow> { + + return run { + return@run combine( + userLocationState, + sortTypeState, + foodCategoryState, + drinkPossibilityState + ) { userLoc, sortType, foodCategory, drinkPossibility -> + getRestaurantsByMapUseCase(sortType, foodCategory, drinkPossibility, userLoc, null, null) + }.distinctUntilChanged() + .flatMapLatest { it } + }.cachedIn(viewModelScope) + } + + suspend fun getMyGroup(): List { + return getMyGroupUseCase() + } } \ No newline at end of file diff --git a/presentation/src/main/res/drawable/ic_jmt_new_logo.xml b/presentation/src/main/res/drawable/ic_jmt_new_logo.xml new file mode 100644 index 00000000..57b8df42 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_jmt_new_logo.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/presentation/src/main/res/layout/content_sheet_empty_group.xml b/presentation/src/main/res/layout/content_sheet_empty_group.xml new file mode 100644 index 00000000..dce5945a --- /dev/null +++ b/presentation/src/main/res/layout/content_sheet_empty_group.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_home.xml b/presentation/src/main/res/layout/fragment_home.xml index 7460a81c..8837e579 100644 --- a/presentation/src/main/res/layout/fragment_home.xml +++ b/presentation/src/main/res/layout/fragment_home.xml @@ -178,6 +178,48 @@ app:layout_constraintTop_toBottomOf="@+id/bottom_sheet_handle_space" app:layout_constraintBottom_toBottomOf="parent"/> + + + + + + + + From ff296b380c5b09315d8c87f1e76f0c678f0d9dc2 Mon Sep 17 00:00:00 2001 From: swit-jim Date: Thu, 14 Mar 2024 22:35:47 +0900 Subject: [PATCH 2/7] =?UTF-8?q?[feat/group=5Fselect]:=20GroupResponse=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/org/gdsc/data/datasource/GroupDataSource.kt | 2 +- .../main/java/org/gdsc/data/datasource/GroupDataSourceImpl.kt | 4 ++-- .../java/org/gdsc/data/model/{GroupResponse.kt => Group.kt} | 2 +- data/src/main/java/org/gdsc/data/network/GroupAPI.kt | 2 +- .../main/java/org/gdsc/data/repository/GroupRepositoryImpl.kt | 4 ++-- .../gdsc/domain/model/response/{GroupResponse.kt => Group.kt} | 2 +- .../main/java/org/gdsc/domain/repository/GroupRepository.kt | 4 ++-- .../java/org/gdsc/presentation/view/home/HomeViewModel.kt | 4 ++-- 8 files changed, 12 insertions(+), 12 deletions(-) rename data/src/main/java/org/gdsc/data/model/{GroupResponse.kt => Group.kt} (95%) rename domain/src/main/java/org/gdsc/domain/model/response/{GroupResponse.kt => Group.kt} (92%) 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 060b6015..f3fa5700 100644 --- a/data/src/main/java/org/gdsc/data/datasource/GroupDataSource.kt +++ b/data/src/main/java/org/gdsc/data/datasource/GroupDataSource.kt @@ -1,6 +1,6 @@ package org.gdsc.data.datasource -import org.gdsc.domain.model.response.GroupResponse +import org.gdsc.domain.model.response.Group interface GroupDataSource { 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 bd93cba9..3796183b 100644 --- a/data/src/main/java/org/gdsc/data/datasource/GroupDataSourceImpl.kt +++ b/data/src/main/java/org/gdsc/data/datasource/GroupDataSourceImpl.kt @@ -2,13 +2,13 @@ package org.gdsc.data.datasource import android.util.Log import org.gdsc.data.network.GroupAPI -import org.gdsc.domain.model.response.GroupResponse +import org.gdsc.domain.model.response.Group import javax.inject.Inject class GroupDataSourceImpl @Inject constructor( private val groupAPI: GroupAPI, ): GroupDataSource { - override suspend fun getMyGroups(): List { + override suspend fun getMyGroups(): List { runCatching { groupAPI.getMyGroups() }.onSuccess { diff --git a/data/src/main/java/org/gdsc/data/model/GroupResponse.kt b/data/src/main/java/org/gdsc/data/model/Group.kt similarity index 95% rename from data/src/main/java/org/gdsc/data/model/GroupResponse.kt rename to data/src/main/java/org/gdsc/data/model/Group.kt index c714d8c0..cc2ac80b 100644 --- a/data/src/main/java/org/gdsc/data/model/GroupResponse.kt +++ b/data/src/main/java/org/gdsc/data/model/Group.kt @@ -2,7 +2,7 @@ package org.gdsc.data.model import com.google.gson.annotations.SerializedName -data class GroupResponse( +data class Group( @SerializedName("groupId") val groupId: Int, @SerializedName("groupIntroduce") val groupIntroduce: String, @SerializedName("groupProfileImageUrl") val groupProfileImageUrl: String, diff --git a/data/src/main/java/org/gdsc/data/network/GroupAPI.kt b/data/src/main/java/org/gdsc/data/network/GroupAPI.kt index 06417a98..e3d39f77 100644 --- a/data/src/main/java/org/gdsc/data/network/GroupAPI.kt +++ b/data/src/main/java/org/gdsc/data/network/GroupAPI.kt @@ -1,7 +1,7 @@ package org.gdsc.data.network import org.gdsc.data.model.Response -import org.gdsc.domain.model.response.GroupResponse +import org.gdsc.domain.model.response.Group import retrofit2.http.GET interface GroupAPI { 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 630e0abd..0011f2b9 100644 --- a/data/src/main/java/org/gdsc/data/repository/GroupRepositoryImpl.kt +++ b/data/src/main/java/org/gdsc/data/repository/GroupRepositoryImpl.kt @@ -1,14 +1,14 @@ package org.gdsc.data.repository import org.gdsc.data.datasource.GroupDataSource -import org.gdsc.domain.model.response.GroupResponse +import org.gdsc.domain.model.response.Group import org.gdsc.domain.repository.GroupRepository import javax.inject.Inject class GroupRepositoryImpl @Inject constructor( private val groupDataSource: GroupDataSource ): GroupRepository { - override suspend fun getMyGroups(): List { + override suspend fun getMyGroups(): List { return groupDataSource.getMyGroups() } diff --git a/domain/src/main/java/org/gdsc/domain/model/response/GroupResponse.kt b/domain/src/main/java/org/gdsc/domain/model/response/Group.kt similarity index 92% rename from domain/src/main/java/org/gdsc/domain/model/response/GroupResponse.kt rename to domain/src/main/java/org/gdsc/domain/model/response/Group.kt index 2740ed4a..6c3727a8 100644 --- a/domain/src/main/java/org/gdsc/domain/model/response/GroupResponse.kt +++ b/domain/src/main/java/org/gdsc/domain/model/response/Group.kt @@ -1,6 +1,6 @@ package org.gdsc.domain.model.response -data class GroupResponse( +data class Group( val groupId: Int, val groupName: String, val groupIntroduce: String, 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 54d51864..f05d97cf 100644 --- a/domain/src/main/java/org/gdsc/domain/repository/GroupRepository.kt +++ b/domain/src/main/java/org/gdsc/domain/repository/GroupRepository.kt @@ -1,7 +1,7 @@ package org.gdsc.domain.repository -import org.gdsc.domain.model.response.GroupResponse +import org.gdsc.domain.model.response.Group interface GroupRepository { - suspend fun getMyGroups(): List + suspend fun getMyGroups(): List } \ No newline at end of file diff --git a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt index 0f7848ca..49f594c1 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt @@ -23,7 +23,7 @@ import org.gdsc.domain.SortType import org.gdsc.domain.model.Location import org.gdsc.domain.model.PagingResult import org.gdsc.domain.model.RegisteredRestaurant -import org.gdsc.domain.model.response.GroupResponse +import org.gdsc.domain.model.response.Group import org.gdsc.domain.usecase.GetMyGroupUseCase import org.gdsc.domain.usecase.GetRestaurantsByMapUseCase import org.gdsc.domain.usecase.token.GetAccessTokenUseCase @@ -126,7 +126,7 @@ class HomeViewModel @Inject constructor( }.cachedIn(viewModelScope) } - suspend fun getMyGroup(): List { + suspend fun getMyGroup(): List { return getMyGroupUseCase() } } \ No newline at end of file From b4e56c0307fad2a8d042e130aa593af608649354 Mon Sep 17 00:00:00 2001 From: swit-jim Date: Thu, 14 Mar 2024 22:35:58 +0900 Subject: [PATCH 3/7] =?UTF-8?q?[feat/group=5Fselect]:=20Group=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gdsc/data/datasource/GroupDataSource.kt | 4 +- .../data/datasource/GroupDataSourceImpl.kt | 11 +++ .../java/org/gdsc/data/network/GroupAPI.kt | 10 ++- .../data/repository/GroupRepositoryImpl.kt | 3 + .../gdsc/domain/repository/GroupRepository.kt | 2 + .../domain/usecase/PostSelectGroupUseCase.kt | 12 +++ .../view/home/GroupSelectAdapter.kt | 41 ++++++++++ .../presentation/view/home/HomeFragment.kt | 80 +++++++++++++++++++ .../presentation/view/home/HomeViewModel.kt | 21 +++-- .../src/main/res/drawable/ic_check_main.xml | 9 +++ .../res/layout/content_sheet_group_select.xml | 24 ++++++ .../src/main/res/layout/item_group_select.xml | 39 +++++++++ 12 files changed, 248 insertions(+), 8 deletions(-) create mode 100644 domain/src/main/java/org/gdsc/domain/usecase/PostSelectGroupUseCase.kt create mode 100644 presentation/src/main/java/org/gdsc/presentation/view/home/GroupSelectAdapter.kt create mode 100644 presentation/src/main/res/drawable/ic_check_main.xml create mode 100644 presentation/src/main/res/layout/content_sheet_group_select.xml create mode 100644 presentation/src/main/res/layout/item_group_select.xml 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 f3fa5700..d6c2680d 100644 --- a/data/src/main/java/org/gdsc/data/datasource/GroupDataSource.kt +++ b/data/src/main/java/org/gdsc/data/datasource/GroupDataSource.kt @@ -4,5 +4,7 @@ import org.gdsc.domain.model.response.Group interface GroupDataSource { - suspend fun getMyGroups(): List + suspend fun getMyGroups(): List + + suspend fun selectGroup(groupId: Int): String } \ 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 3796183b..9dc80400 100644 --- a/data/src/main/java/org/gdsc/data/datasource/GroupDataSourceImpl.kt +++ b/data/src/main/java/org/gdsc/data/datasource/GroupDataSourceImpl.kt @@ -18,4 +18,15 @@ class GroupDataSourceImpl @Inject constructor( } return emptyList() } + + override suspend fun selectGroup(groupId: Int): String { + runCatching { + groupAPI.selectGroup(groupId) + }.onSuccess { + return it.data + }.onFailure { + return "" + } + return "" + } } \ No newline at end of file diff --git a/data/src/main/java/org/gdsc/data/network/GroupAPI.kt b/data/src/main/java/org/gdsc/data/network/GroupAPI.kt index e3d39f77..4cbdd088 100644 --- a/data/src/main/java/org/gdsc/data/network/GroupAPI.kt +++ b/data/src/main/java/org/gdsc/data/network/GroupAPI.kt @@ -3,9 +3,17 @@ package org.gdsc.data.network import org.gdsc.data.model.Response import org.gdsc.domain.model.response.Group import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Path interface GroupAPI { @GET("api/v1/group/my") - suspend fun getMyGroups(): Response> + suspend fun getMyGroups(): Response> + + @POST("api/v1/group/{groupId}/select") + suspend fun selectGroup( + @Path("groupId") groupId: Int + ): Response + } \ 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 0011f2b9..4174d164 100644 --- a/data/src/main/java/org/gdsc/data/repository/GroupRepositoryImpl.kt +++ b/data/src/main/java/org/gdsc/data/repository/GroupRepositoryImpl.kt @@ -12,4 +12,7 @@ class GroupRepositoryImpl @Inject constructor( return groupDataSource.getMyGroups() } + override suspend fun selectGroup(groupId: Int): String { + return groupDataSource.selectGroup(groupId) + } } \ 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 f05d97cf..2a95577f 100644 --- a/domain/src/main/java/org/gdsc/domain/repository/GroupRepository.kt +++ b/domain/src/main/java/org/gdsc/domain/repository/GroupRepository.kt @@ -4,4 +4,6 @@ import org.gdsc.domain.model.response.Group interface GroupRepository { suspend fun getMyGroups(): List + + suspend fun selectGroup(groupId: Int): String } \ No newline at end of file diff --git a/domain/src/main/java/org/gdsc/domain/usecase/PostSelectGroupUseCase.kt b/domain/src/main/java/org/gdsc/domain/usecase/PostSelectGroupUseCase.kt new file mode 100644 index 00000000..7d157e19 --- /dev/null +++ b/domain/src/main/java/org/gdsc/domain/usecase/PostSelectGroupUseCase.kt @@ -0,0 +1,12 @@ +package org.gdsc.domain.usecase + +import org.gdsc.domain.repository.GroupRepository +import javax.inject.Inject + +class PostSelectGroupUseCase @Inject constructor( + private val groupRepository: GroupRepository +) { + suspend operator fun invoke(groupId: Int): String { + return groupRepository.selectGroup(groupId) + } +} \ No newline at end of file diff --git a/presentation/src/main/java/org/gdsc/presentation/view/home/GroupSelectAdapter.kt b/presentation/src/main/java/org/gdsc/presentation/view/home/GroupSelectAdapter.kt new file mode 100644 index 00000000..3187f3fe --- /dev/null +++ b/presentation/src/main/java/org/gdsc/presentation/view/home/GroupSelectAdapter.kt @@ -0,0 +1,41 @@ +package org.gdsc.presentation.view.home + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import org.gdsc.domain.model.response.Group +import org.gdsc.presentation.base.BaseViewHolder +import org.gdsc.presentation.base.ViewHolderBindListener +import org.gdsc.presentation.databinding.ItemGroupSelectBinding + +class GroupSelectAdapter( + private val listener: ViewHolderBindListener +): RecyclerView.Adapter() { + + private val groupList = mutableListOf() + + override fun onBindViewHolder(holder: GroupSelectViewHolder, position: Int) { + holder.bind(groupList[position]) + } + + class GroupSelectViewHolder( + binding: ItemGroupSelectBinding, + listener: ViewHolderBindListener + ): BaseViewHolder(binding, listener) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GroupSelectViewHolder { + return GroupSelectViewHolder( + ItemGroupSelectBinding.inflate( + LayoutInflater.from(parent.context), parent, false + ), listener + ) + } + + override fun getItemCount(): Int = groupList.size + + fun submitList(list: List) { + groupList.clear() + groupList.addAll(list) + notifyDataSetChanged() + } +} \ No newline at end of file diff --git a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt index 5cf989c6..8c24a43f 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt @@ -6,6 +6,8 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView import androidx.core.content.res.ResourcesCompat import androidx.core.view.isVisible import androidx.fragment.app.Fragment @@ -14,6 +16,7 @@ import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.viewbinding.ViewBinding +import com.bumptech.glide.Glide import com.google.android.material.bottomsheet.BottomSheetBehavior import com.naver.maps.geometry.LatLng import com.naver.maps.map.CameraUpdate @@ -32,10 +35,12 @@ import org.gdsc.domain.FoodCategory import org.gdsc.domain.SortType import org.gdsc.domain.model.Location import org.gdsc.domain.model.RegisteredRestaurant +import org.gdsc.domain.model.response.Group import org.gdsc.presentation.R import org.gdsc.presentation.base.BaseViewHolder import org.gdsc.presentation.base.ViewHolderBindListener import org.gdsc.presentation.databinding.ContentSheetEmptyGroupBinding +import org.gdsc.presentation.databinding.ContentSheetGroupSelectBinding import org.gdsc.presentation.databinding.FragmentHomeBinding import org.gdsc.presentation.utils.repeatWhenUiStarted import org.gdsc.presentation.view.WebViewActivity @@ -141,6 +146,60 @@ class HomeFragment : Fragment(), ViewHolderBindListener { if (standardBottomSheetBehavior.state != BottomSheetBehavior.STATE_COLLAPSED) standardBottomSheetBehavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED } + + binding.groupArrow.setOnClickListener { + + repeatWhenUiStarted { + viewModel.getMyGroup().let { groupList -> + + viewModel.setGroupList(groupList) + } + } + + BottomSheetDialog(requireContext()) + .bindBuilder( + ContentSheetGroupSelectBinding.inflate(LayoutInflater.from(requireContext())) + ) { dialog -> + + val listener: ViewHolderBindListener = object : ViewHolderBindListener { + override fun onViewHolderBind( + holder: BaseViewHolder, + _item: Any + ) { + if (holder is GroupSelectAdapter.GroupSelectViewHolder && _item is Group) { + with(holder.itemView) { + findViewById(R.id.group_name).text = _item.groupName + findViewById(R.id.select_button).isVisible = _item.isSelected + Glide.with(this).load(_item.groupProfileImageUrl).into(findViewById(R.id.group_image)) + + setOnClickListener { + // TODO GroupList : 추후에 Room에 Current Group 넣어주게 되면, 반영해야 하는 부분 + repeatWhenUiStarted { + viewModel.selectGroup(_item.groupId) + } + binding.groupName.text = _item.groupName + dialog.dismiss() + } + } + + } + } + } + + with(dialog) { + val groupSelectAdapter = GroupSelectAdapter(listener) + + groupSelectAdapter.submitList(viewModel.myGroupList.value ?: emptyList()) + + findViewById(R.id.group_select_recycler_view)?.apply { + adapter = groupSelectAdapter + layoutManager = LinearLayoutManager(requireContext()) + } + + show() + } + } + } } override fun onViewHolderBind(holder: BaseViewHolder, _item: Any) { @@ -178,11 +237,31 @@ class HomeFragment : Fragment(), ViewHolderBindListener { } } } + + if (holder is GroupSelectAdapter.GroupSelectViewHolder && _item is Group) { + with(holder.itemView) { + findViewById(R.id.group_name).text = _item.groupName + findViewById(R.id.select_button).isVisible = _item.isSelected + Glide.with(this).load(_item.groupProfileImageUrl).into(findViewById(R.id.group_image)) + + setOnClickListener { + repeatWhenUiStarted { + viewModel.selectGroup(_item.groupId) + } + // TODO GroupList : 추후에 Room에 Current Group 넣어주게 되면, 반영해야 하는 부분 + binding.groupName.text = _item.groupName + } + } + + } } private fun setGroup() { repeatWhenUiStarted { viewModel.getMyGroup().let { groupList -> + + viewModel.setGroupList(groupList) + if (groupList.isEmpty()) { BottomSheetDialog(requireContext()) .bindBuilder( @@ -217,6 +296,7 @@ class HomeFragment : Fragment(), ViewHolderBindListener { // 선택 된 그룹이 없는 경우 if (groupList.isNotEmpty()) { binding.groupName.text = groupList[0].groupName + viewModel.selectGroup(groupList[0].groupId) } } } diff --git a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt index 49f594c1..a2984482 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt @@ -1,13 +1,9 @@ package org.gdsc.presentation.view.home -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.PagingData import androidx.paging.cachedIn -import androidx.paging.map -import com.google.gson.Gson -import com.naver.maps.geometry.LatLng import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -21,12 +17,11 @@ import org.gdsc.domain.DrinkPossibility import org.gdsc.domain.FoodCategory import org.gdsc.domain.SortType import org.gdsc.domain.model.Location -import org.gdsc.domain.model.PagingResult import org.gdsc.domain.model.RegisteredRestaurant import org.gdsc.domain.model.response.Group import org.gdsc.domain.usecase.GetMyGroupUseCase import org.gdsc.domain.usecase.GetRestaurantsByMapUseCase -import org.gdsc.domain.usecase.token.GetAccessTokenUseCase +import org.gdsc.domain.usecase.PostSelectGroupUseCase import org.gdsc.presentation.JmtLocationManager import javax.inject.Inject @@ -35,6 +30,7 @@ class HomeViewModel @Inject constructor( private val locationManager: JmtLocationManager, private val getRestaurantsByMapUseCase: GetRestaurantsByMapUseCase, private val getMyGroupUseCase: GetMyGroupUseCase, + private val postSelectGroupUserCase: PostSelectGroupUseCase, ) : ViewModel() { suspend fun getCurrentLocation() = locationManager.getCurrentLocation() @@ -65,6 +61,11 @@ class HomeViewModel @Inject constructor( val drinkPossibilityState: StateFlow get() = _drinkPossibilityState + private var _myGroupList = MutableStateFlow>(emptyList()) + val myGroupList: StateFlow> + get() = _myGroupList + + fun setUserLocation(userLocation: Location) { _userLocationState.value = userLocation @@ -90,6 +91,10 @@ class HomeViewModel @Inject constructor( _drinkPossibilityState.value = drinkPossibility } + fun setGroupList(groupList: List) { + _myGroupList.value = groupList + } + @OptIn(ExperimentalCoroutinesApi::class) suspend fun registeredPagingData(): Flow> { @@ -129,4 +134,8 @@ class HomeViewModel @Inject constructor( suspend fun getMyGroup(): List { return getMyGroupUseCase() } + + suspend fun selectGroup(groupID: Int) { + postSelectGroupUserCase(groupID) + } } \ No newline at end of file diff --git a/presentation/src/main/res/drawable/ic_check_main.xml b/presentation/src/main/res/drawable/ic_check_main.xml new file mode 100644 index 00000000..058ef62d --- /dev/null +++ b/presentation/src/main/res/drawable/ic_check_main.xml @@ -0,0 +1,9 @@ + + + diff --git a/presentation/src/main/res/layout/content_sheet_group_select.xml b/presentation/src/main/res/layout/content_sheet_group_select.xml new file mode 100644 index 00000000..0028ec28 --- /dev/null +++ b/presentation/src/main/res/layout/content_sheet_group_select.xml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/item_group_select.xml b/presentation/src/main/res/layout/item_group_select.xml new file mode 100644 index 00000000..a2d2f415 --- /dev/null +++ b/presentation/src/main/res/layout/item_group_select.xml @@ -0,0 +1,39 @@ + + + + + + + + + + \ No newline at end of file From 821b6ac2dedab92a986702696a3407209c381518 Mon Sep 17 00:00:00 2001 From: swit-jim Date: Thu, 14 Mar 2024 23:32:38 +0900 Subject: [PATCH 4/7] =?UTF-8?q?[feat/group=5Fselect]:=20Group=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20ResultState=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/view/home/HomeFragment.kt | 126 +++++++++++------- .../presentation/view/home/HomeViewModel.kt | 15 ++- 2 files changed, 87 insertions(+), 54 deletions(-) diff --git a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt index 8c24a43f..f383f60d 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt @@ -3,6 +3,7 @@ package org.gdsc.presentation.view.home import android.content.Intent import android.graphics.PointF import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -28,6 +29,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import org.gdsc.domain.DrinkPossibility @@ -42,6 +44,7 @@ import org.gdsc.presentation.base.ViewHolderBindListener import org.gdsc.presentation.databinding.ContentSheetEmptyGroupBinding import org.gdsc.presentation.databinding.ContentSheetGroupSelectBinding import org.gdsc.presentation.databinding.FragmentHomeBinding +import org.gdsc.presentation.model.ResultState import org.gdsc.presentation.utils.repeatWhenUiStarted import org.gdsc.presentation.view.WebViewActivity import org.gdsc.presentation.view.custom.BottomSheetDialog @@ -75,7 +78,6 @@ class HomeFragment : Fragment(), ViewHolderBindListener { ): View { _binding = FragmentHomeBinding.inflate(inflater, container, false) - setGroup() setMap(savedInstanceState) observeState() @@ -148,10 +150,8 @@ class HomeFragment : Fragment(), ViewHolderBindListener { } binding.groupArrow.setOnClickListener { - repeatWhenUiStarted { viewModel.getMyGroup().let { groupList -> - viewModel.setGroupList(groupList) } } @@ -176,8 +176,8 @@ class HomeFragment : Fragment(), ViewHolderBindListener { // TODO GroupList : 추후에 Room에 Current Group 넣어주게 되면, 반영해야 하는 부분 repeatWhenUiStarted { viewModel.selectGroup(_item.groupId) + viewModel.setCurrentGroup(_item) } - binding.groupName.text = _item.groupName dialog.dismiss() } } @@ -188,8 +188,14 @@ class HomeFragment : Fragment(), ViewHolderBindListener { with(dialog) { val groupSelectAdapter = GroupSelectAdapter(listener) - - groupSelectAdapter.submitList(viewModel.myGroupList.value ?: emptyList()) + viewModel.myGroupList.value.let { + when(it) { + is ResultState.OnSuccess -> { + groupSelectAdapter.submitList(it.response ?: emptyList()) + } + else -> {} + } + } findViewById(R.id.group_select_recycler_view)?.apply { adapter = groupSelectAdapter @@ -237,67 +243,77 @@ class HomeFragment : Fragment(), ViewHolderBindListener { } } } - - if (holder is GroupSelectAdapter.GroupSelectViewHolder && _item is Group) { - with(holder.itemView) { - findViewById(R.id.group_name).text = _item.groupName - findViewById(R.id.select_button).isVisible = _item.isSelected - Glide.with(this).load(_item.groupProfileImageUrl).into(findViewById(R.id.group_image)) - - setOnClickListener { - repeatWhenUiStarted { - viewModel.selectGroup(_item.groupId) - } - // TODO GroupList : 추후에 Room에 Current Group 넣어주게 되면, 반영해야 하는 부분 - binding.groupName.text = _item.groupName - } - } - - } } private fun setGroup() { repeatWhenUiStarted { viewModel.getMyGroup().let { groupList -> - viewModel.setGroupList(groupList) + } + } + + repeatWhenUiStarted { + viewModel.myGroupList.collect { state -> + when(state) { + is ResultState.OnSuccess -> { + val groupList = state.response - if (groupList.isEmpty()) { - BottomSheetDialog(requireContext()) - .bindBuilder( - ContentSheetEmptyGroupBinding.inflate(LayoutInflater.from(requireContext())) - ) { dialog -> - with(dialog) { - binding.groupHeader.isVisible = false - dialog.setCancelable(false) - dialog.behavior.isDraggable = false - createGroupButton.setOnClickListener { - startActivity( - Intent(requireContext(), WebViewActivity::class.java) - ) + if (groupList.isEmpty()) { + viewModel.setCurrentGroup(null) + } else { + groupList.forEach { + if (it.isSelected) { + viewModel.setCurrentGroup(it) + return@forEach } - show() } } - } else { - binding.groupHeader.isVisible = true - groupList.forEach { - if (it.isSelected) { - binding.groupName.text = it.groupName - if (it.restaurantCnt == 0) { + } + + else -> {} + } + } + } + + repeatWhenUiStarted { + viewModel.currentGroup.collect { state -> + when(state) { + is ResultState.OnSuccess -> { + val group = state.response + if (group == null) { + BottomSheetDialog(requireContext()) + .bindBuilder( + ContentSheetEmptyGroupBinding.inflate(LayoutInflater.from(requireContext())) + ) { dialog -> + with(dialog) { + binding.groupHeader.isVisible = false + dialog.setCancelable(false) + dialog.behavior.isDraggable = false + createGroupButton.setOnClickListener { + startActivity( + Intent(requireContext(), WebViewActivity::class.java) + ) + } + show() + } + } + } else { + binding.groupHeader.isVisible = true + binding.groupName.text = group.groupName + binding.groupImage.apply { + Glide.with(this.context).load(group.groupProfileImageUrl) + .into(this) + invalidate() + } + + if (group.restaurantCnt == 0) { binding.recyclerView.isVisible = false binding.registGroup.isVisible = true } - return@forEach } } - - // 선택 된 그룹이 없는 경우 - if (groupList.isNotEmpty()) { - binding.groupName.text = groupList[0].groupName - viewModel.selectGroup(groupList[0].groupId) - } + else -> {} } } } @@ -372,6 +388,9 @@ class HomeFragment : Fragment(), ViewHolderBindListener { } private fun observeState() { + + setGroup() + repeatWhenUiStarted { viewModel.registeredPagingData().collect { mapMarkerAdapter.submitData(it) @@ -382,6 +401,11 @@ class HomeFragment : Fragment(), ViewHolderBindListener { viewModel.setSortType(SortType.DISTANCE) } + repeatWhenUiStarted { + viewModel.myGroupList.collect { + binding.groupName.text + } + } } override fun onDestroyView() { diff --git a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt index a2984482..87dbe34d 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt @@ -23,6 +23,7 @@ import org.gdsc.domain.usecase.GetMyGroupUseCase import org.gdsc.domain.usecase.GetRestaurantsByMapUseCase import org.gdsc.domain.usecase.PostSelectGroupUseCase import org.gdsc.presentation.JmtLocationManager +import org.gdsc.presentation.model.ResultState import javax.inject.Inject @HiltViewModel @@ -61,11 +62,15 @@ class HomeViewModel @Inject constructor( val drinkPossibilityState: StateFlow get() = _drinkPossibilityState - private var _myGroupList = MutableStateFlow>(emptyList()) - val myGroupList: StateFlow> + private var _myGroupList = MutableStateFlow>>(ResultState.OnLoading()) + val myGroupList: StateFlow>> get() = _myGroupList + private var _currentGroup = MutableStateFlow>(ResultState.OnLoading()) + val currentGroup: StateFlow> + get() = _currentGroup + fun setUserLocation(userLocation: Location) { _userLocationState.value = userLocation @@ -92,7 +97,11 @@ class HomeViewModel @Inject constructor( } fun setGroupList(groupList: List) { - _myGroupList.value = groupList + _myGroupList.value = ResultState.OnSuccess(groupList) + } + + fun setCurrentGroup(group: Group?) { + _currentGroup.value = ResultState.OnSuccess(group) } From 9760878d95dbaac2bc41d960baf825ada2fbc863 Mon Sep 17 00:00:00 2001 From: swit-jim Date: Thu, 14 Mar 2024 23:32:59 +0900 Subject: [PATCH 5/7] =?UTF-8?q?[feat/group=5Fselect]:=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/view/home/HomeFragment.kt | 234 +++++++++--------- 1 file changed, 120 insertions(+), 114 deletions(-) diff --git a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt index f383f60d..21a5b29d 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt @@ -94,7 +94,7 @@ class HomeFragment : Fragment(), ViewHolderBindListener { mapMarkerAdapter ) } -// setRecyclerView() + return binding.root } @@ -105,6 +105,51 @@ class HomeFragment : Fragment(), ViewHolderBindListener { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + setRestaurantListBottomSheet() + setGroup() + + } + + override fun onViewHolderBind(holder: BaseViewHolder, _item: Any) { + if (holder is RestaurantFilterAdapter.RestaurantFilterViewHolder) { + + val sortSpinner = holder.itemView.findViewById(R.id.sort_spinner) + val foodSpinner = holder.itemView.findViewById(R.id.food_category_spinner) + val drinkSpinner = holder.itemView.findViewById(R.id.drink_possibility_spinner) + + sortSpinner.setMenu(SortType.getAllText()) { + viewModel.setSortType(SortType.values()[it.itemId]) + } + + foodSpinner.setMenu(FoodCategory.getAllText()) { + viewModel.setFoodCategory(FoodCategory.values()[it.itemId]) + } + + drinkSpinner.setMenu(DrinkPossibility.getAllText()) { + viewModel.setDrinkPossibility(DrinkPossibility.values()[it.itemId]) + } + + repeatWhenUiStarted { + viewModel.sortTypeState.collectLatest { + sortSpinner.setMenuTitle(it.text) + } + } + repeatWhenUiStarted { + viewModel.foodCategoryState.collectLatest { + foodSpinner.setMenuTitle(it.text) + } + } + repeatWhenUiStarted { + viewModel.drinkPossibilityState.collectLatest { + drinkSpinner.setMenuTitle(it.text) + } + } + } + } + + + private fun setRestaurantListBottomSheet() { standardBottomSheetBehavior.addBottomSheetCallback( object : BottomSheetBehavior.BottomSheetCallback() { fun setBottomSheetRelatedView(isVisible: Boolean) { @@ -148,7 +193,9 @@ class HomeFragment : Fragment(), ViewHolderBindListener { if (standardBottomSheetBehavior.state != BottomSheetBehavior.STATE_COLLAPSED) standardBottomSheetBehavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED } + } + private fun setGroup() { binding.groupArrow.setOnClickListener { repeatWhenUiStarted { viewModel.getMyGroup().let { groupList -> @@ -208,117 +255,6 @@ class HomeFragment : Fragment(), ViewHolderBindListener { } } - override fun onViewHolderBind(holder: BaseViewHolder, _item: Any) { - if (holder is RestaurantFilterAdapter.RestaurantFilterViewHolder) { - - val sortSpinner = holder.itemView.findViewById(R.id.sort_spinner) - val foodSpinner = holder.itemView.findViewById(R.id.food_category_spinner) - val drinkSpinner = holder.itemView.findViewById(R.id.drink_possibility_spinner) - - sortSpinner.setMenu(SortType.getAllText()) { - viewModel.setSortType(SortType.values()[it.itemId]) - } - - foodSpinner.setMenu(FoodCategory.getAllText()) { - viewModel.setFoodCategory(FoodCategory.values()[it.itemId]) - } - - drinkSpinner.setMenu(DrinkPossibility.getAllText()) { - viewModel.setDrinkPossibility(DrinkPossibility.values()[it.itemId]) - } - - repeatWhenUiStarted { - viewModel.sortTypeState.collectLatest { - sortSpinner.setMenuTitle(it.text) - } - } - repeatWhenUiStarted { - viewModel.foodCategoryState.collectLatest { - foodSpinner.setMenuTitle(it.text) - } - } - repeatWhenUiStarted { - viewModel.drinkPossibilityState.collectLatest { - drinkSpinner.setMenuTitle(it.text) - } - } - } - } - - private fun setGroup() { - repeatWhenUiStarted { - viewModel.getMyGroup().let { groupList -> - viewModel.setGroupList(groupList) - } - } - - repeatWhenUiStarted { - viewModel.myGroupList.collect { state -> - when(state) { - is ResultState.OnSuccess -> { - val groupList = state.response - - if (groupList.isEmpty()) { - viewModel.setCurrentGroup(null) - } else { - groupList.forEach { - if (it.isSelected) { - viewModel.setCurrentGroup(it) - return@forEach - } - } - } - } - - else -> {} - } - } - } - - repeatWhenUiStarted { - viewModel.currentGroup.collect { state -> - when(state) { - is ResultState.OnSuccess -> { - val group = state.response - if (group == null) { - BottomSheetDialog(requireContext()) - .bindBuilder( - ContentSheetEmptyGroupBinding.inflate(LayoutInflater.from(requireContext())) - ) { dialog -> - with(dialog) { - binding.groupHeader.isVisible = false - dialog.setCancelable(false) - dialog.behavior.isDraggable = false - createGroupButton.setOnClickListener { - startActivity( - Intent(requireContext(), WebViewActivity::class.java) - ) - } - show() - } - } - } else { - binding.groupHeader.isVisible = true - binding.groupName.text = group.groupName - binding.groupImage.apply { - Glide.with(this.context).load(group.groupProfileImageUrl) - .into(this) - invalidate() - } - - if (group.restaurantCnt == 0) { - - binding.recyclerView.isVisible = false - binding.registGroup.isVisible = true - } - } - } - else -> {} - } - } - } - } - private fun setMap(savedInstanceState: Bundle?) { mapView = binding.mapView mapView.onCreate(savedInstanceState) @@ -389,8 +325,6 @@ class HomeFragment : Fragment(), ViewHolderBindListener { private fun observeState() { - setGroup() - repeatWhenUiStarted { viewModel.registeredPagingData().collect { mapMarkerAdapter.submitData(it) @@ -406,6 +340,78 @@ class HomeFragment : Fragment(), ViewHolderBindListener { binding.groupName.text } } + + repeatWhenUiStarted { + viewModel.getMyGroup().let { groupList -> + viewModel.setGroupList(groupList) + } + } + + repeatWhenUiStarted { + viewModel.myGroupList.collect { state -> + when(state) { + is ResultState.OnSuccess -> { + val groupList = state.response + + if (groupList.isEmpty()) { + viewModel.setCurrentGroup(null) + } else { + groupList.forEach { + if (it.isSelected) { + viewModel.setCurrentGroup(it) + return@forEach + } + } + } + } + + else -> {} + } + } + } + + repeatWhenUiStarted { + viewModel.currentGroup.collect { state -> + when(state) { + is ResultState.OnSuccess -> { + val group = state.response + if (group == null) { + BottomSheetDialog(requireContext()) + .bindBuilder( + ContentSheetEmptyGroupBinding.inflate(LayoutInflater.from(requireContext())) + ) { dialog -> + with(dialog) { + binding.groupHeader.isVisible = false + dialog.setCancelable(false) + dialog.behavior.isDraggable = false + createGroupButton.setOnClickListener { + startActivity( + Intent(requireContext(), WebViewActivity::class.java) + ) + } + show() + } + } + } else { + binding.groupHeader.isVisible = true + binding.groupName.text = group.groupName + binding.groupImage.apply { + Glide.with(this.context).load(group.groupProfileImageUrl) + .into(this) + invalidate() + } + + if (group.restaurantCnt == 0) { + + binding.recyclerView.isVisible = false + binding.registGroup.isVisible = true + } + } + } + else -> {} + } + } + } } override fun onDestroyView() { From 5941adb3d88fb1b58613f330b9af18649bf348c6 Mon Sep 17 00:00:00 2001 From: swit-jim Date: Fri, 15 Mar 2024 00:56:19 +0900 Subject: [PATCH 6/7] =?UTF-8?q?[feat/group=5Fselect]:=20=EB=93=B1=EB=A1=9D?= =?UTF-8?q?=20=EB=90=9C=20=EC=8B=9D=EB=8B=B9=20=EA=B0=80=EC=A0=B8=EC=98=A4?= =?UTF-8?q?=EA=B8=B0=20API=20=EA=B7=B8=EB=A3=B9=20id=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../database/RestaurantByMapPagingSource.kt | 1 + .../data/datasource/RestaurantDataSource.kt | 9 ++- .../datasource/RestaurantDataSourceImpl.kt | 5 +- .../repository/RestaurantRepositoryImpl.kt | 7 ++- .../org/gdsc/domain/model/ScreenLocation.kt | 6 ++ .../model/request/RestaurantSearchRequest.kt | 2 + .../domain/repository/RestaurantRepository.kt | 9 ++- .../usecase/GetRestaurantsByMapUseCase.kt | 19 +++++- .../presentation/view/home/HomeViewModel.kt | 58 ++++++++++--------- 9 files changed, 81 insertions(+), 35 deletions(-) create mode 100644 domain/src/main/java/org/gdsc/domain/model/ScreenLocation.kt diff --git a/data/src/main/java/org/gdsc/data/database/RestaurantByMapPagingSource.kt b/data/src/main/java/org/gdsc/data/database/RestaurantByMapPagingSource.kt index 01ffc2c3..2768f349 100644 --- a/data/src/main/java/org/gdsc/data/database/RestaurantByMapPagingSource.kt +++ b/data/src/main/java/org/gdsc/data/database/RestaurantByMapPagingSource.kt @@ -6,6 +6,7 @@ import org.gdsc.data.model.RegisteredRestaurantResponse import org.gdsc.data.network.RestaurantAPI import org.gdsc.domain.SortType import org.gdsc.domain.model.request.RestaurantSearchRequest +import org.gdsc.domain.model.response.Group class RestaurantByMapPagingSource( private val api: RestaurantAPI, diff --git a/data/src/main/java/org/gdsc/data/datasource/RestaurantDataSource.kt b/data/src/main/java/org/gdsc/data/datasource/RestaurantDataSource.kt index bb8ffcea..c0417a97 100644 --- a/data/src/main/java/org/gdsc/data/datasource/RestaurantDataSource.kt +++ b/data/src/main/java/org/gdsc/data/datasource/RestaurantDataSource.kt @@ -15,6 +15,7 @@ import org.gdsc.domain.model.RestaurantLocationInfo import org.gdsc.domain.model.UserLocation import org.gdsc.domain.model.request.ModifyRestaurantInfoRequest import org.gdsc.domain.model.request.RestaurantRegistrationRequest +import org.gdsc.domain.model.response.Group import org.gdsc.domain.model.response.RestaurantInfoResponse interface RestaurantDataSource { @@ -39,7 +40,13 @@ interface RestaurantDataSource { suspend fun putRestaurantInfo(putRestaurantInfoRequest: ModifyRestaurantInfoRequest): String suspend fun getRestaurantsByMap( - userLocation: Location?, startLocation: Location?, endLocation: Location?, sortType: SortType, foodCategory: FoodCategory?, drinkPossibility: DrinkPossibility? + userLocation: Location?, + startLocation: Location?, + endLocation: Location?, + sortType: SortType, + foodCategory: FoodCategory?, + drinkPossibility: DrinkPossibility?, + currentGroup: Group?, ): Flow> suspend fun getRegisteredRestaurantsBySearch(keyword: String?, userLocation: Location?): Flow> diff --git a/data/src/main/java/org/gdsc/data/datasource/RestaurantDataSourceImpl.kt b/data/src/main/java/org/gdsc/data/datasource/RestaurantDataSourceImpl.kt index e3a0507a..2faa69fb 100644 --- a/data/src/main/java/org/gdsc/data/datasource/RestaurantDataSourceImpl.kt +++ b/data/src/main/java/org/gdsc/data/datasource/RestaurantDataSourceImpl.kt @@ -34,6 +34,7 @@ import org.gdsc.domain.model.UserLocation import org.gdsc.domain.model.request.ModifyRestaurantInfoRequest import org.gdsc.domain.model.request.RestaurantRegistrationRequest import org.gdsc.domain.model.request.RestaurantSearchRequest +import org.gdsc.domain.model.response.Group import org.gdsc.domain.model.response.RestaurantInfoResponse import retrofit2.HttpException import javax.inject.Inject @@ -203,12 +204,14 @@ class RestaurantDataSourceImpl @Inject constructor( endLocation: Location?, sortType: SortType, foodCategory: FoodCategory?, - drinkPossibility: DrinkPossibility? + drinkPossibility: DrinkPossibility?, + currentGroup: Group?, ): Flow> { val restaurantSearchRequest = RestaurantSearchRequest( userLocation = userLocation, startLocation = startLocation, endLocation = endLocation, + groupId = currentGroup?.groupId, filter = Filter( categoryFilter = when (foodCategory) { FoodCategory.INIT, FoodCategory.ETC -> String.Empty diff --git a/data/src/main/java/org/gdsc/data/repository/RestaurantRepositoryImpl.kt b/data/src/main/java/org/gdsc/data/repository/RestaurantRepositoryImpl.kt index 3f4a7319..aa582bab 100644 --- a/data/src/main/java/org/gdsc/data/repository/RestaurantRepositoryImpl.kt +++ b/data/src/main/java/org/gdsc/data/repository/RestaurantRepositoryImpl.kt @@ -17,6 +17,7 @@ import org.gdsc.domain.model.Review import org.gdsc.domain.model.UserLocation import org.gdsc.domain.model.request.ModifyRestaurantInfoRequest import org.gdsc.domain.model.request.RestaurantRegistrationRequest +import org.gdsc.domain.model.response.Group import org.gdsc.domain.model.response.RestaurantInfoResponse import org.gdsc.domain.repository.RestaurantRepository import javax.inject.Inject @@ -103,7 +104,8 @@ class RestaurantRepositoryImpl @Inject constructor( endLocation: Location?, sortType: SortType, foodCategory: FoodCategory?, - drinkPossibility: DrinkPossibility? + drinkPossibility: DrinkPossibility?, + currentGroup: Group?, ): Flow> { return restaurantDataSource.getRestaurantsByMap( userLocation, @@ -111,7 +113,8 @@ class RestaurantRepositoryImpl @Inject constructor( endLocation, sortType, foodCategory, - drinkPossibility + drinkPossibility, + currentGroup, ).map { result -> result.map { restaurant -> RegisteredRestaurant( diff --git a/domain/src/main/java/org/gdsc/domain/model/ScreenLocation.kt b/domain/src/main/java/org/gdsc/domain/model/ScreenLocation.kt new file mode 100644 index 00000000..11e06ced --- /dev/null +++ b/domain/src/main/java/org/gdsc/domain/model/ScreenLocation.kt @@ -0,0 +1,6 @@ +package org.gdsc.domain.model + +data class ScreenLocation( + val startLocation: Location, + val endLocation: Location, +) \ No newline at end of file diff --git a/domain/src/main/java/org/gdsc/domain/model/request/RestaurantSearchRequest.kt b/domain/src/main/java/org/gdsc/domain/model/request/RestaurantSearchRequest.kt index d35e2750..f072bf2c 100644 --- a/domain/src/main/java/org/gdsc/domain/model/request/RestaurantSearchRequest.kt +++ b/domain/src/main/java/org/gdsc/domain/model/request/RestaurantSearchRequest.kt @@ -13,6 +13,8 @@ data class RestaurantSearchRequest( val startLocation: Location? = null, @SerializedName("endLocation") val endLocation: Location? = null, + @SerializedName("groupId") + val groupId: Int? = null, @SerializedName("keyword") val keyword: String? = null, ) diff --git a/domain/src/main/java/org/gdsc/domain/repository/RestaurantRepository.kt b/domain/src/main/java/org/gdsc/domain/repository/RestaurantRepository.kt index 77eb872f..80e609d2 100644 --- a/domain/src/main/java/org/gdsc/domain/repository/RestaurantRepository.kt +++ b/domain/src/main/java/org/gdsc/domain/repository/RestaurantRepository.kt @@ -14,6 +14,7 @@ import org.gdsc.domain.model.Review import org.gdsc.domain.model.UserLocation import org.gdsc.domain.model.request.ModifyRestaurantInfoRequest import org.gdsc.domain.model.request.RestaurantRegistrationRequest +import org.gdsc.domain.model.response.Group import org.gdsc.domain.model.response.RestaurantInfoResponse interface RestaurantRepository { @@ -38,7 +39,13 @@ interface RestaurantRepository { suspend fun putRestaurantInfo(putRestaurantInfoRequest: ModifyRestaurantInfoRequest): String suspend fun getRestaurantsByMap( - userLocation: Location?, startLocation: Location?, endLocation: Location?, sortType: SortType, foodCategory: FoodCategory?, drinkPossibility: DrinkPossibility? + userLocation: Location?, + startLocation: Location?, + endLocation: Location?, + sortType: SortType, + foodCategory: FoodCategory?, + drinkPossibility: DrinkPossibility?, + currentGroup: Group?, ): Flow> suspend fun getRegisteredRestaurantsBySearch(keyword: String?, userLocation: Location?): Flow> diff --git a/domain/src/main/java/org/gdsc/domain/usecase/GetRestaurantsByMapUseCase.kt b/domain/src/main/java/org/gdsc/domain/usecase/GetRestaurantsByMapUseCase.kt index 828287df..cb3a9a96 100644 --- a/domain/src/main/java/org/gdsc/domain/usecase/GetRestaurantsByMapUseCase.kt +++ b/domain/src/main/java/org/gdsc/domain/usecase/GetRestaurantsByMapUseCase.kt @@ -7,6 +7,7 @@ import org.gdsc.domain.FoodCategory import org.gdsc.domain.SortType import org.gdsc.domain.model.Location import org.gdsc.domain.model.RegisteredRestaurant +import org.gdsc.domain.model.response.Group import org.gdsc.domain.repository.RestaurantRepository import javax.inject.Inject @@ -14,9 +15,23 @@ class GetRestaurantsByMapUseCase @Inject constructor( private val restaurantRepository: RestaurantRepository ) { suspend operator fun invoke( - sortType: SortType, foodCategory: FoodCategory?, drinkPossibility: DrinkPossibility?, userLocation: Location?, startLocation: Location? = null, endLocation: Location? = null + sortType: SortType, + foodCategory: FoodCategory?, + drinkPossibility: DrinkPossibility?, + userLocation: Location?, + startLocation: Location? = null, + endLocation: Location? = null, + currentGroup: Group? = null, ): Flow> { - return restaurantRepository.getRestaurantsByMap( userLocation, startLocation, endLocation, sortType, foodCategory, drinkPossibility) + return restaurantRepository.getRestaurantsByMap( + userLocation, + startLocation, + endLocation, + sortType, + foodCategory, + drinkPossibility, + currentGroup, + ) } } \ No newline at end of file diff --git a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt index d03e9582..0f331a0d 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeViewModel.kt @@ -1,5 +1,6 @@ package org.gdsc.presentation.view.home +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.PagingData @@ -9,16 +10,19 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.stateIn import org.gdsc.domain.DrinkPossibility import org.gdsc.domain.FoodCategory import org.gdsc.domain.SortType import org.gdsc.domain.model.Location import org.gdsc.domain.model.RegisteredRestaurant +import org.gdsc.domain.model.ScreenLocation import org.gdsc.domain.model.response.Group import org.gdsc.domain.usecase.GetMyGroupUseCase import org.gdsc.domain.usecase.GetRestaurantsByMapUseCase @@ -46,11 +50,24 @@ class HomeViewModel @Inject constructor( val startLocationState: StateFlow get() = _startLocationState - private var _endLocationState = MutableStateFlow(Location("0", "0")) val endLocationState: StateFlow get() = _endLocationState + + val _screenLocationState: StateFlow = + combine( + startLocationState, + endLocationState + ) { startLocationState, endLocationState -> + ScreenLocation(startLocationState, endLocationState) + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), ScreenLocation(Location("0", "0"), Location("0", "0"))) + + val screenLocationState: StateFlow + get() = _screenLocationState + + + private var _sortTypeState = MutableStateFlow(SortType.DISTANCE) val sortTypeState: StateFlow get() = _sortTypeState @@ -68,8 +85,8 @@ class HomeViewModel @Inject constructor( get() = _myGroupList - private var _currentGroup = MutableStateFlow>(ResultState.OnLoading()) - val currentGroup: StateFlow> + private var _currentGroup = MutableStateFlow(null) + val currentGroup: StateFlow get() = _currentGroup @@ -102,7 +119,7 @@ class HomeViewModel @Inject constructor( } fun setCurrentGroup(group: Group?) { - _currentGroup.value = ResultState.OnSuccess(group) + _currentGroup.value = group } @@ -113,29 +130,13 @@ class HomeViewModel @Inject constructor( return run { return@run combine( - startLocationState, - endLocationState, - sortTypeState, - foodCategoryState, - drinkPossibilityState - ) { startLoc, endLoc, sortType, foodCategory, drinkPossibility -> - getRestaurantsByMapUseCase(sortType, foodCategory, drinkPossibility, userLoc, startLoc, endLoc) - }.distinctUntilChanged() - .flatMapLatest { it } - }.cachedIn(viewModelScope) - } - - @OptIn(ExperimentalCoroutinesApi::class) - suspend fun registeredPagingDataByGroup(): Flow> { - - return run { - return@run combine( - userLocationState, + screenLocationState, sortTypeState, foodCategoryState, - drinkPossibilityState - ) { userLoc, sortType, foodCategory, drinkPossibility -> - getRestaurantsByMapUseCase(sortType, foodCategory, drinkPossibility, userLoc, null, null) + drinkPossibilityState, + currentGroup, + ) { screenLoc, sortType, foodCategory, drinkPossibility, group -> + getRestaurantsByMapUseCase(sortType, foodCategory, drinkPossibility, userLoc, screenLoc.startLocation, screenLoc.endLocation, group) }.distinctUntilChanged() .flatMapLatest { it } }.cachedIn(viewModelScope) @@ -149,9 +150,10 @@ class HomeViewModel @Inject constructor( userLocationState, sortTypeState, foodCategoryState, - drinkPossibilityState - ) { userLoc, sortType, foodCategory, drinkPossibility -> - getRestaurantsByMapUseCase(sortType, foodCategory, drinkPossibility, userLoc, null, null) + drinkPossibilityState, + currentGroup, + ) { userLoc, sortType, foodCategory, drinkPossibility, group -> + getRestaurantsByMapUseCase(sortType, foodCategory, drinkPossibility, userLoc, null, null, group) }.distinctUntilChanged() .flatMapLatest { it } }.cachedIn(viewModelScope) From 6079be580e80f927ad5d71a840fbca69c3519587 Mon Sep 17 00:00:00 2001 From: swit-jim Date: Fri, 15 Mar 2024 00:56:40 +0900 Subject: [PATCH 7/7] =?UTF-8?q?[feat/group=5Fselect]:=20=EB=B7=B0=20Visibl?= =?UTF-8?q?e=20=EC=A0=9C=EC=95=BD=20=EC=A1=B0=EA=B1=B4=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/view/home/HomeFragment.kt | 124 ++++++++---------- .../src/main/res/layout/fragment_home.xml | 83 ++++++------ 2 files changed, 100 insertions(+), 107 deletions(-) diff --git a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt index c3c9a7f7..051282dd 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/home/HomeFragment.kt @@ -147,19 +147,6 @@ class HomeFragment : Fragment(), ViewHolderBindListener { } } - - private fun setRestaurantListBottomSheet() { - standardBottomSheetBehavior.addBottomSheetCallback( - object : BottomSheetBehavior.BottomSheetCallback() { - fun setBottomSheetRelatedView(isVisible: Boolean) { - binding.bottomSheetHandle.isVisible = isVisible - binding.bottomSheetHandleSpace.isVisible = isVisible - binding.mapOptionContainer.isVisible = isVisible - } - - setRestaurantListBottomSheet() - } - private fun setGroup() { binding.groupArrow.setOnClickListener { repeatWhenUiStarted { @@ -305,37 +292,38 @@ class HomeFragment : Fragment(), ViewHolderBindListener { standardBottomSheetBehavior.addBottomSheetCallback( object : BottomSheetBehavior.BottomSheetCallback() { - fun setBottomSheetRelatedView(isVisible: Boolean) { - binding.bottomSheetHandle.isVisible = isVisible - binding.bottomSheetHandleSpace.isVisible = isVisible - binding.mapOptionContainer.isVisible = isVisible + fun setBottomSheetRelatedView(isExpanded: Boolean) { + viewModel.currentGroup.value.let { + val isNotFullExpanded = isExpanded.not() || it == null || it.restaurantCnt == 0 + + with(binding) { + binding.bottomSheetActionButtons.isVisible = isExpanded && it != null && it.restaurantCnt != 0 + bottomSheetHandle.isVisible = isNotFullExpanded + bottomSheetHandleSpace.isVisible = isNotFullExpanded + mapOptionContainer.isVisible = isNotFullExpanded + groupHeader.elevation = if (isNotFullExpanded) 0F else 10F + bottomSheet.background = + ResourcesCompat.getDrawable( + resources, + if (isNotFullExpanded) + R.drawable.bg_bottom_sheet_top_round + else + R.color.white, + null + ) + + } + } } override fun onStateChanged(bottomSheet: View, newState: Int) { when (newState) { BottomSheetBehavior.STATE_EXPANDED -> { - binding.groupHeader.elevation = 10F - binding.bottomSheetActionButtons.isVisible = true - - setBottomSheetRelatedView(false) - binding.bottomSheet.background = ResourcesCompat.getDrawable( - resources, - R.color.white, - null - ) - } - BottomSheetBehavior.STATE_HALF_EXPANDED -> { - binding.mapOptionContainer.isVisible = true + setBottomSheetRelatedView(true) } BottomSheetBehavior.STATE_DRAGGING -> { - binding.groupHeader.elevation = 0F - setBottomSheetRelatedView(true) - binding.bottomSheet.background = ResourcesCompat.getDrawable( - resources, - R.drawable.bg_bottom_sheet_top_round, - null - ) + setBottomSheetRelatedView(false) } } } @@ -380,29 +368,9 @@ class HomeFragment : Fragment(), ViewHolderBindListener { is ResultState.OnSuccess -> { val groupList = state.response - if (groupList.isEmpty()) { + if (groupList.isNullOrEmpty()) { viewModel.setCurrentGroup(null) - } else { - groupList.forEach { - if (it.isSelected) { - viewModel.setCurrentGroup(it) - return@forEach - } - } - } - } - else -> {} - } - } - } - - repeatWhenUiStarted { - viewModel.currentGroup.collect { state -> - when(state) { - is ResultState.OnSuccess -> { - val group = state.response - if (group == null) { BottomSheetDialog(requireContext()) .bindBuilder( ContentSheetEmptyGroupBinding.inflate(LayoutInflater.from(requireContext())) @@ -420,25 +388,43 @@ class HomeFragment : Fragment(), ViewHolderBindListener { } } } else { - binding.groupHeader.isVisible = true - binding.groupName.text = group.groupName - binding.groupImage.apply { - Glide.with(this.context).load(group.groupProfileImageUrl) - .into(this) - invalidate() - } - - if (group.restaurantCnt == 0) { - - binding.recyclerView.isVisible = false - binding.registGroup.isVisible = true + groupList.forEach { + if (it.isSelected) { + viewModel.setCurrentGroup(it) + return@forEach + } } } } + else -> {} } } } + + repeatWhenUiStarted { + viewModel.currentGroup.collect { + if (it != null) { + binding.groupHeader.isVisible = true + binding.groupName.text = it.groupName + binding.groupImage.apply { + Glide.with(this.context).load(it.groupProfileImageUrl) + .into(this) + } + + if (it.restaurantCnt == 0) { + binding.bottomSheet.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT + binding.recyclerView.isVisible = false + binding.registLayout.isVisible = true + } else { + binding.bottomSheet.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT + binding.recyclerView.isVisible = true + binding.registLayout.isVisible = false + } + } + } + + } } override fun onDestroyView() { diff --git a/presentation/src/main/res/layout/fragment_home.xml b/presentation/src/main/res/layout/fragment_home.xml index 869f4f87..959a44f5 100644 --- a/presentation/src/main/res/layout/fragment_home.xml +++ b/presentation/src/main/res/layout/fragment_home.xml @@ -178,47 +178,54 @@ app:layout_constraintTop_toBottomOf="@+id/bottom_sheet_handle_space" app:layout_constraintBottom_toBottomOf="parent"/> - - - + android:gravity="center" + app:layout_constraintVertical_bias="0" + android:paddingBottom="@dimen/bottom_navigation_height_with_item" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + android:orientation="vertical"> - + - + + + +