Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] 장소 검색 API 연동 #106

Merged
merged 20 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a985683
[add] #92 RequestPlanLocationDto, ResponsePlanLocationDto 추가
HAJIEUN02 Jan 10, 2024
af8ebb9
Merge branch 'develop' into feat-plan-location-api
HAJIEUN02 Jan 10, 2024
6ce978b
[chore] #92 BaseRespone 반영해 Dto 수정
HAJIEUN02 Jan 10, 2024
ff3cc01
[feat] #92 HiltViewModel, AndroidEntryPoint 어노테이션 추가
HAJIEUN02 Jan 10, 2024
8d7446a
[feat] #92 클린아키텍처 및 DI 적용한 Repository, UseCase, DataSource,Service 추가
HAJIEUN02 Jan 10, 2024
5ea16de
[feat] #92 단일선택 로직을 제외한 서버통신 구현
HAJIEUN02 Jan 10, 2024
6b7e477
Merge branch 'develop' into feat-plan-location-api
HAJIEUN02 Jan 11, 2024
296ee41
[feat] #102 번개 생성 API 연동
HAJIEUN02 Jan 11, 2024
3c3131a
[mod] #102 엠티뷰, 버튼 비활성화 로직 수정
HAJIEUN02 Jan 11, 2024
20a127a
[chore] #102 ktlint 적용
HAJIEUN02 Jan 11, 2024
009ba19
[feat] #92 번개 생성 API 연동
HAJIEUN02 Jan 11, 2024
e03ec5f
[mod] #92 엠티뷰, 버튼 비활성화 로직 수정
HAJIEUN02 Jan 11, 2024
ed0741e
[chore] #92 ktlint 적용
HAJIEUN02 Jan 11, 2024
1138b7e
Merge branch 'develop' into feat-plan-location-api
HAJIEUN02 Jan 11, 2024
6198abd
Merge remote-tracking branch 'origin/feat-plan-location-api' into fea…
HAJIEUN02 Jan 11, 2024
016b037
[chore] #102 ktlint 적용
HAJIEUN02 Jan 11, 2024
cd848b2
[chore] #92 코드리뷰 반영
HAJIEUN02 Jan 11, 2024
15ed996
[chore] #92 ktlint 적용
HAJIEUN02 Jan 11, 2024
7e5ffc4
Merge branch 'develop' into feat-plan-location-api
HAJIEUN02 Jan 11, 2024
a047539
[chore] #92 코드리뷰 반영 및 ktlint 적용
HAJIEUN02 Jan 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.sopt.pingle.data.datasource.remote

import org.sopt.pingle.data.model.remote.response.ResponsePlanDto
import org.sopt.pingle.util.base.BaseResponse

interface PlanRemoteDataSource {
suspend fun getPlanLocation(searchWord: String): BaseResponse<List<ResponsePlanDto>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.sopt.pingle.data.datasourceimpl.remote

import javax.inject.Inject
import org.sopt.pingle.data.datasource.remote.PlanRemoteDataSource
import org.sopt.pingle.data.model.remote.response.ResponsePlanDto
import org.sopt.pingle.data.service.PlanService
import org.sopt.pingle.util.base.BaseResponse

class PlanRemoteDataSourceImpl @Inject constructor(
private val planService: PlanService
) : PlanRemoteDataSource {
override suspend fun getPlanLocation(searchWord: String): BaseResponse<List<ResponsePlanDto>> =
planService.getPlanLocationList(searchWord = searchWord)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.sopt.pingle.data.model.remote.request

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class RequestPlanDto(
@SerialName("search")
val search: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.sopt.pingle.data.model.remote.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import org.sopt.pingle.domain.model.PlanLocationEntity

@Serializable
data class ResponsePlanDto(
@SerialName("x")
val xCoordinate: Double,
@SerialName("y")
val yCoordinate: Double,
@SerialName("location")
val locationName: String,
@SerialName("address")
val locationAddress: String,
@SerialName("roadAddress")
val locationRoadAddress: String
) {
fun toPlanLocationEntity() = PlanLocationEntity(
location = locationName,
address = locationAddress,
x = xCoordinate,
y = yCoordinate
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.sopt.pingle.data.repository

import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import org.sopt.pingle.data.datasource.remote.PlanRemoteDataSource
import org.sopt.pingle.domain.model.PlanLocationEntity
import org.sopt.pingle.domain.repository.PlanRepository

class PlanRepositoryImpl @Inject constructor(
private val planRemoteDataSource: PlanRemoteDataSource
) : PlanRepository {
override suspend fun getPlanLocationList(searchWord: String): Flow<List<PlanLocationEntity>> =
flow {
val result = runCatching {
planRemoteDataSource.getPlanLocation(searchWord = searchWord).data.map { planLocation ->
planLocation.toPlanLocationEntity()
}
}
emit(result.getOrThrow())
}
}
19 changes: 19 additions & 0 deletions app/src/main/java/org/sopt/pingle/data/service/PlanService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.sopt.pingle.data.service

import org.sopt.pingle.data.model.remote.response.ResponsePlanDto
import org.sopt.pingle.util.base.BaseResponse
import retrofit2.http.GET
import retrofit2.http.Query

interface PlanService {
@GET("$VERSION/$LOCATION")
suspend fun getPlanLocationList(
@Query(SEARCH_WORD) searchWord: String
): BaseResponse<List<ResponsePlanDto>>

companion object {
const val VERSION = "v1"
const val LOCATION = "location"
const val SEARCH_WORD = "search"
}
}
6 changes: 6 additions & 0 deletions app/src/main/java/org/sopt/pingle/di/DataSourceModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import org.sopt.pingle.data.datasource.remote.AuthRemoteDataSource
import org.sopt.pingle.data.datasource.remote.DummyRemoteDataSource
import org.sopt.pingle.data.datasource.remote.MapRemoteDataSource
import org.sopt.pingle.data.datasource.remote.PingleRemoteDataSource
import org.sopt.pingle.data.datasource.remote.PlanRemoteDataSource
import org.sopt.pingle.data.datasourceimpl.local.DummyLocalDataSourceImpl
import org.sopt.pingle.data.datasourceimpl.local.PingleLocalDataSourceImpl
import org.sopt.pingle.data.datasourceimpl.remote.AuthRemoteDataSourceImpl
import org.sopt.pingle.data.datasourceimpl.remote.DummyRemoteDataSourceImpl
import org.sopt.pingle.data.datasourceimpl.remote.MapRemoteDataSourceImpl
import org.sopt.pingle.data.datasourceimpl.remote.PingleRemoteDataSourceImpl
import org.sopt.pingle.data.datasourceimpl.remote.PlanRemoteDataSourceImpl

@Module
@InstallIn(SingletonComponent::class)
Expand Down Expand Up @@ -44,4 +46,8 @@ abstract class DataSourceModule {
@Binds
@Singleton
abstract fun bindsPingleLocalDataSource(pingleLocalDataSourceImpl: PingleLocalDataSourceImpl): PingleLocalDataSource

@Binds
@Singleton
abstract fun bindsPlanRemoteDataSource(planRemoteDataSourceImpl: PlanRemoteDataSourceImpl): PlanRemoteDataSource
}
6 changes: 6 additions & 0 deletions app/src/main/java/org/sopt/pingle/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import org.sopt.pingle.data.repository.AuthRepositoryImpl
import org.sopt.pingle.data.repository.DummyRepositoryImpl
import org.sopt.pingle.data.repository.MapRepositoryImpl
import org.sopt.pingle.data.repository.PingleRepositoryImpl
import org.sopt.pingle.data.repository.PlanRepositoryImpl
import org.sopt.pingle.domain.repository.AuthRepository
import org.sopt.pingle.domain.repository.DummyRepository
import org.sopt.pingle.domain.repository.MapRepository
import org.sopt.pingle.domain.repository.PingleRepository
import org.sopt.pingle.domain.repository.PlanRepository

@Module
@InstallIn(SingletonComponent::class)
Expand All @@ -32,4 +34,8 @@ abstract class RepositoryModule {
@Binds
@Singleton
abstract fun bindsPingleRepository(pingleRepositoryImpl: PingleRepositoryImpl): PingleRepository

@Binds
@Singleton
abstract fun bindsPlanRepository(planRepositoryImpl: PlanRepositoryImpl): PlanRepository
}
6 changes: 6 additions & 0 deletions app/src/main/java/org/sopt/pingle/di/ServiceModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.sopt.pingle.data.service.AuthService
import org.sopt.pingle.data.service.DummyService
import org.sopt.pingle.data.service.MapService
import org.sopt.pingle.data.service.PingleService
import org.sopt.pingle.data.service.PlanService
import org.sopt.pingle.di.qualifier.Pingle
import retrofit2.Retrofit
import retrofit2.create
Expand All @@ -35,4 +36,9 @@ object ServiceModule {
@Singleton
fun providesPingleService(@Pingle retrofit: Retrofit): PingleService =
retrofit.create(PingleService::class.java)

@Provides
@Singleton
fun providesPlanService(@Pingle retrofit: Retrofit): PlanService =
retrofit.create(PlanService::class.java)
}
7 changes: 7 additions & 0 deletions app/src/main/java/org/sopt/pingle/di/UseCaseModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import javax.inject.Singleton
import org.sopt.pingle.domain.repository.DummyRepository
import org.sopt.pingle.domain.repository.MapRepository
import org.sopt.pingle.domain.repository.PingleRepository
import org.sopt.pingle.domain.repository.PlanRepository
import org.sopt.pingle.domain.usecase.GetDummyUserListUseCase
import org.sopt.pingle.domain.usecase.GetPinListWithoutFilteringUseCase
import org.sopt.pingle.domain.usecase.GetPingleListUseCase
import org.sopt.pingle.domain.usecase.GetPlanLocationListUseCase
import org.sopt.pingle.domain.usecase.PostPingleCancelUseCase
import org.sopt.pingle.domain.usecase.PostPingleJoinUseCase
import org.sopt.pingle.domain.usecase.SetDummyDataUseCase
Expand Down Expand Up @@ -47,4 +49,9 @@ class UseCaseModule {
@Singleton
fun providesPostPingleCancelUseCase(pingleRepository: PingleRepository): PostPingleCancelUseCase =
PostPingleCancelUseCase(pingleRepository = pingleRepository)

@Provides
@Singleton
fun providesGetPlanLocationListUseCase(planRepository: PlanRepository): GetPlanLocationListUseCase =
GetPlanLocationListUseCase(planRepository = planRepository)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.sopt.pingle.domain.repository

import kotlinx.coroutines.flow.Flow
import org.sopt.pingle.domain.model.PlanLocationEntity

interface PlanRepository {
suspend fun getPlanLocationList(searchWord: String): Flow<List<PlanLocationEntity>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.sopt.pingle.domain.usecase

import kotlinx.coroutines.flow.Flow
import org.sopt.pingle.domain.model.PlanLocationEntity
import org.sopt.pingle.domain.repository.PlanRepository

class GetPlanLocationListUseCase(
private val planRepository: PlanRepository
) {
suspend operator fun invoke(searchWord: String): Flow<List<PlanLocationEntity>> =
planRepository.getPlanLocationList(searchWord = searchWord)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.sopt.pingle.presentation.model

import androidx.databinding.ObservableBoolean
import org.sopt.pingle.domain.model.PlanLocationEntity

data class PlanLocationModel(
val planLocation: PlanLocationEntity,
var isSelected: ObservableBoolean = ObservableBoolean(false)
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.widget.ViewPager2
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.sopt.pingle.R
Expand All @@ -21,6 +22,7 @@ import org.sopt.pingle.presentation.ui.main.plan.plantitle.PlanTitleFragment
import org.sopt.pingle.util.base.BindingActivity
import org.sopt.pingle.util.component.AllModalDialogFragment

@AndroidEntryPoint
class PlanActivity : BindingActivity<ActivityPlanBinding>(R.layout.activity_plan) {
private val planViewModel: PlanViewModel by viewModels()
private lateinit var fragmentList: ArrayList<Fragment>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,29 @@ package org.sopt.pingle.presentation.ui.main.plan

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.sopt.pingle.domain.model.PlanLocationEntity
import org.sopt.pingle.domain.usecase.GetPlanLocationListUseCase
import org.sopt.pingle.presentation.type.CategoryType
import org.sopt.pingle.presentation.type.PlanType
import org.sopt.pingle.util.combineAll
import org.sopt.pingle.util.view.UiState

class PlanViewModel : ViewModel() {
@HiltViewModel
class PlanViewModel @Inject constructor(
private val getPlanLocationListUseCase: GetPlanLocationListUseCase
) : ViewModel() {
private val _currentPage = MutableStateFlow(FIRST_PAGE_POSITION)
val currentPage get() = _currentPage.asStateFlow()
val planTitle = MutableStateFlow("")
Expand All @@ -29,15 +40,22 @@ class PlanViewModel : ViewModel() {
val planOpenChattingLink = MutableStateFlow("")
val planSummary = MutableStateFlow("")

private val _selectedLocation = MutableStateFlow<PlanLocationEntity?>(null)
val selectedLocation get() = _selectedLocation.asStateFlow()

private val _selectedCategory = MutableStateFlow<CategoryType?>(null)
val selectedCategory get() = _selectedCategory.asStateFlow()

private val _selectedRecruitment = MutableStateFlow<String?>("1")
val selectedRecruitment get() = _selectedRecruitment.asStateFlow()

private val _planLocationListState =
MutableSharedFlow<UiState<List<PlanLocationEntity>>>()
val planLocationListState get() = _planLocationListState.asSharedFlow()

private val _planLocationList = MutableStateFlow<List<PlanLocationEntity>>(emptyList())
val planLocationList get() = _planLocationList.asStateFlow()

private val _selectedLocation = MutableStateFlow<PlanLocationEntity?>(null)
val selectedLocation get() = _selectedLocation.asStateFlow()

val isPlanBtnEnabled: StateFlow<Boolean> = listOf(
currentPage,
selectedCategory,
Expand Down Expand Up @@ -112,8 +130,29 @@ class PlanViewModel : ViewModel() {

private var oldPosition = DEFAULT_OLD_POSITION

private val _planLocationList = MutableStateFlow<List<PlanLocationEntity>>(emptyList())
val planLocationList get() = _planLocationList.asStateFlow()
fun getPlanLocationList(searchWord: String) {
viewModelScope.launch {
_planLocationListState.emit(UiState.Loading)
_planLocationList.value = emptyList()
_selectedLocation.value = null
runCatching {
getPlanLocationListUseCase(searchWord).collect() { planLocationList ->
when (planLocationList.isEmpty()) {
true -> {
_planLocationListState.emit(UiState.Empty)
}

false -> {
_planLocationList.value = planLocationList
_planLocationListState.emit(UiState.Success(planLocationList))
}
Comment on lines +140 to +148
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

개인적인 생각으로 조건문이 Boolean이라면 when문 보다는 if else로 하는 것이 더 깔끔할 것 같다는 생각이 들어요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 코틀린에만 when이 있어서 이걸 쓰구 싶음여 ㅋ

}
}
}.onFailure { exception: Throwable ->
_planLocationListState.emit(UiState.Error(exception.message))
}
}
}

fun updatePlanLocationList(position: Int) {
when (oldPosition) {
Expand All @@ -131,63 +170,17 @@ class PlanViewModel : ViewModel() {
setIsSelected(position)
}
}
_selectedLocation.value = if (getIsSelected(position)) _planLocationList.value[position] else null
_selectedLocation.value =
if (getIsSelected(position)) _planLocationList.value[position] else null
oldPosition = position
}

// 이전 값이 -> 초기값 + 셀렉티드 값이 있으면
fun checkIsNull(): Boolean {
return _planLocationList.value.isEmpty()
// TODO return planLocationList.value.isEmpty()
}

private fun setIsSelected(position: Int) {
_planLocationList.value[position].isSelected.set(!_planLocationList.value[position].isSelected.get())
}

private fun getIsSelected(position: Int) = _planLocationList.value[position].isSelected.get()

init {
_planLocationList.value = listOf(
PlanLocationEntity(
location = "하얀집",
address = "서울 중구 퇴계로6길 12",
x = 123.5,
y = 56.7
),
PlanLocationEntity(
location = "하얀집2호점",
address = "서울 중구 퇴계로6길 12",
x = 123.5,
y = 56.7
),
PlanLocationEntity(
location = "하얀집3호점",
address = "서울 중구 퇴계로6길 12",
x = 123.5,
y = 56.7
),
PlanLocationEntity(
location = "하얀집 싫어싫어싫어",
address = "서울 중구 퇴계로6길 12",
x = 123.5,
y = 56.7
),
PlanLocationEntity(
location = "하얀집 좋아좋아좋아",
address = "서울 중구 퇴계로6길 12",
x = 123.5,
y = 56.7
),
PlanLocationEntity(
location = "하얀집웅시러",
address = "서울 중구 퇴계로6길 12",
x = 123.5,
y = 56.7
)
)
}

companion object {
const val FIRST_PAGE_POSITION = 0
const val DEFAULT_OLD_POSITION = -1
Expand Down
Loading
Loading