-
Notifications
You must be signed in to change notification settings - Fork 2
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] 핀 목록 (좌표 필터링x) API 연동 #95
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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.ResponsePinListDto | ||
import org.sopt.pingle.util.base.BaseResponse | ||
|
||
interface MapRemoteDataSource { | ||
suspend fun getPinListWithoutFiltering(teamId: Long, category: String?): BaseResponse<List<ResponsePinListDto>> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package org.sopt.pingle.data.datasourceimpl.remote | ||
|
||
import javax.inject.Inject | ||
import org.sopt.pingle.data.datasource.remote.MapRemoteDataSource | ||
import org.sopt.pingle.data.model.remote.response.ResponsePinListDto | ||
import org.sopt.pingle.data.service.MapService | ||
import org.sopt.pingle.util.base.BaseResponse | ||
|
||
class MapRemoteDataSourceImpl @Inject constructor( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Inject 어노테이션은 디펜던시에 힐트 설정 안 하면 못 쓰는 앤가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네 인젝션은 힐트에서 사용하는 어노테이션이기때문에 힐트를 사용할 때만 가능한 것으로 알고있습니다 |
||
private val mapService: MapService | ||
) : MapRemoteDataSource { | ||
override suspend fun getPinListWithoutFiltering( | ||
teamId: Long, | ||
category: String? | ||
): BaseResponse<List<ResponsePinListDto>> = | ||
mapService.getPinListWithoutFiltering(teamId = teamId, category = category) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 데이터소스에서 함수 추상화하고 데이터소스Impl에서 서비스에 접근해서 통신한 값들 불러오는 함수를 호출하는 거까지 이해 완... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 구현해야할 코드가 없고 서버통신에서 필요한 부분이 어노테이션과 매개변수에 있어서 그런게 아닐까요..?! |
||
} |
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 RequestPinListDto( | ||
@SerialName("category") | ||
val category: String? | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package org.sopt.pingle.data.model.remote.response | ||
|
||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
import org.sopt.pingle.domain.model.PinEntity | ||
|
||
@Serializable | ||
data class ResponsePinListDto( | ||
@SerialName("id") | ||
val id: Long, | ||
@SerialName("x") | ||
val x: Double, | ||
@SerialName("y") | ||
val y: Double, | ||
@SerialName("category") | ||
val category: String, | ||
@SerialName("meetingCount") | ||
val meetingCount: Int | ||
) { | ||
fun toPinEntity() = PinEntity( | ||
id = this.id, | ||
x = this.x, | ||
y = this.y, | ||
category = this.category, | ||
meetingCount = this.meetingCount | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
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.MapRemoteDataSource | ||
import org.sopt.pingle.domain.model.PinEntity | ||
import org.sopt.pingle.domain.repository.MapRepository | ||
|
||
class MapRepositoryImpl @Inject constructor( | ||
private val mapDataSource: MapRemoteDataSource | ||
) : MapRepository { | ||
override suspend fun getPinListWithoutFiltering( | ||
teamId: Long, | ||
category: String? | ||
): Flow<List<PinEntity>> = flow { | ||
val result = runCatching { | ||
mapDataSource.getPinListWithoutFiltering( | ||
teamId = teamId, | ||
category = category | ||
).data.map { pin -> | ||
pin.toPinEntity() | ||
} | ||
Comment on lines
+21
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이런식으로 하는거 몰랐었는데 코드짜면서 막힐 때 보고 배웠습니닷! |
||
} | ||
emit(result.getOrThrow()) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package org.sopt.pingle.data.service | ||
|
||
import org.sopt.pingle.data.model.remote.response.ResponsePinListDto | ||
import org.sopt.pingle.util.base.BaseResponse | ||
import retrofit2.http.GET | ||
import retrofit2.http.Path | ||
import retrofit2.http.Query | ||
|
||
interface MapService { | ||
@GET("$VERSION/$TEAMS/{$TEAM_ID}/$PINS") | ||
suspend fun getPinListWithoutFiltering( | ||
@Path("$TEAM_ID") teamId: Long, | ||
@Query(CATEGORY) category: String? | ||
): BaseResponse<List<ResponsePinListDto>> | ||
|
||
companion object { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이건 URI에서 중복되는 부분들 일부러 다 상수화한건가요? 이렇게 해야 하는 이유는 무엇인가여? 몇 개 없을 때 상수화하는게 가독성에 조을까요? 그냥 나중에 규모가 커졌을 때를 대비해서 미리 상수화하는건가여 |
||
const val VERSION = "v1" | ||
const val TEAMS = "teams" | ||
const val PINS = "pins" | ||
const val TEAM_ID = "teamId" | ||
const val CATEGORY = "category" | ||
} | ||
Comment on lines
+16
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분 상수화를 모든 Service 파일마다 해야하는건가요!? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 더 궁금한점이 나중에 url이 바뀔 가능성과 가독성을 위한거라면 상수화한 파일만 따로만들어서 서버통신 부분에서 공통되게 사용해야하는게 맞는 방법이라고 생각이 들어요! |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,8 +7,10 @@ import dagger.hilt.components.SingletonComponent | |
import javax.inject.Singleton | ||
import org.sopt.pingle.data.datasource.local.DummyLocalDataSource | ||
import org.sopt.pingle.data.datasource.remote.DummyRemoteDataSource | ||
import org.sopt.pingle.data.datasource.remote.MapRemoteDataSource | ||
import org.sopt.pingle.data.datasourceimpl.local.DummyLocalDataSourceImpl | ||
import org.sopt.pingle.data.datasourceimpl.remote.DummyRemoteDataSourceImpl | ||
import org.sopt.pingle.data.datasourceimpl.remote.MapRemoteDataSourceImpl | ||
|
||
@Module | ||
@InstallIn(SingletonComponent::class) | ||
|
@@ -20,4 +22,8 @@ abstract class DataSourceModule { | |
@Binds | ||
@Singleton | ||
abstract fun bindsDummyRemoteDataSource(dummyRemoteDataSourceImpl: DummyRemoteDataSourceImpl): DummyRemoteDataSource | ||
|
||
@Binds | ||
@Singleton | ||
abstract fun bindsMapRemoteDataSource(mapRemoteDataSourceImpl: MapRemoteDataSourceImpl): MapRemoteDataSource | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분이 제일 이해가 안되는데................................. 힐트 부분인건가요? 대충 뭘 많이 모아둔.. 데이터소스를 모아둔 부분인 건 알겠는데요......... |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,12 +6,18 @@ import dagger.hilt.InstallIn | |
import dagger.hilt.components.SingletonComponent | ||
import javax.inject.Singleton | ||
import org.sopt.pingle.data.repository.DummyRepositoryImpl | ||
import org.sopt.pingle.data.repository.MapRepositoryImpl | ||
import org.sopt.pingle.domain.repository.DummyRepository | ||
import org.sopt.pingle.domain.repository.MapRepository | ||
|
||
@Module | ||
@InstallIn(SingletonComponent::class) | ||
abstract class RepositoryModule { | ||
@Binds | ||
@Singleton | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @singleton 객체의 인스턴스가 1개만 생성되는 싱글톤 패턴을 구현하기 위해 존재하는 어노테이션인가요? 이 어노테이션의 의미가 궁금합니다링구 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 다은언니와 공부완 |
||
abstract fun bindsDummyRepository(dummyRepositoryImpl: DummyRepositoryImpl): DummyRepository | ||
|
||
@Binds | ||
@Singleton | ||
abstract fun bindsMapRepository(mapRepositoryImpl: MapRepositoryImpl): MapRepository | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,8 +6,10 @@ import dagger.hilt.InstallIn | |
import dagger.hilt.components.SingletonComponent | ||
import javax.inject.Singleton | ||
import org.sopt.pingle.data.service.DummyService | ||
import org.sopt.pingle.data.service.MapService | ||
import org.sopt.pingle.di.qualifier.Pingle | ||
import retrofit2.Retrofit | ||
import retrofit2.create | ||
|
||
@Module | ||
@InstallIn(SingletonComponent::class) | ||
|
@@ -16,4 +18,9 @@ object ServiceModule { | |
@Singleton | ||
fun providesDummyService(@Pingle retrofit: Retrofit): DummyService = | ||
retrofit.create(DummyService::class.java) | ||
|
||
@Provides | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혼자 공부완ㅋㅅㅋ |
||
@Singleton | ||
fun providesMapService(@Pingle retrofit: Retrofit): MapService = | ||
retrofit.create(MapService::class.java) | ||
} |
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.PinEntity | ||
|
||
interface MapRepository { | ||
suspend fun getPinListWithoutFiltering(teamId: Long, category: String?): Flow<List<PinEntity>> | ||
} |
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.PinEntity | ||
import org.sopt.pingle.domain.repository.MapRepository | ||
|
||
class GetPinListWithoutFilteringUseCase( | ||
private val mapRepository: MapRepository | ||
) { | ||
suspend operator fun invoke(teamId: Long, category: String?): Flow<List<PinEntity>> = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 왜 함수 이름 선언 안 해주고 invoke를 쓰는 건가염? invoke의 이점이 무엇인지 궁금합니둥 |
||
mapRepository.getPinListWithoutFiltering(teamId = teamId, category = category) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,21 +21,23 @@ import com.naver.maps.map.NaverMap | |
import com.naver.maps.map.OnMapReadyCallback | ||
import com.naver.maps.map.overlay.OverlayImage | ||
import com.naver.maps.map.util.FusedLocationSource | ||
import dagger.hilt.android.AndroidEntryPoint | ||
import kotlinx.coroutines.flow.launchIn | ||
import kotlinx.coroutines.flow.onEach | ||
import org.sopt.pingle.R | ||
import org.sopt.pingle.databinding.FragmentMapBinding | ||
import org.sopt.pingle.presentation.model.MarkerModel | ||
import org.sopt.pingle.presentation.type.CategoryType | ||
import org.sopt.pingle.presentation.ui.main.home.mainlist.MainListFragment | ||
import org.sopt.pingle.util.base.BindingFragment | ||
import org.sopt.pingle.util.component.AllModalDialogFragment | ||
import org.sopt.pingle.util.component.OnPingleCardClickListener | ||
import org.sopt.pingle.util.component.PingleChip | ||
import org.sopt.pingle.util.fragment.navigateToFragment | ||
import org.sopt.pingle.util.fragment.navigateToWebView | ||
import org.sopt.pingle.util.fragment.showToast | ||
import org.sopt.pingle.util.fragment.stringOf | ||
import org.sopt.pingle.util.view.UiState | ||
|
||
@AndroidEntryPoint | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @androidentrypoint 는 프로젝트의 각 Android 클래스에 관한 개별 Hilt 구성요소를 생성합니다....... 대충 뇌빼고 따라 쳐야 하는 부분 그냥 규칙.. 머 그런거겠죠 |
||
class MapFragment : BindingFragment<FragmentMapBinding>(R.layout.fragment_map), OnMapReadyCallback { | ||
private val mapViewModel by viewModels<MapViewModel>() | ||
private lateinit var naverMap: NaverMap | ||
|
@@ -63,6 +65,7 @@ class MapFragment : BindingFragment<FragmentMapBinding>(R.layout.fragment_map), | |
this.naverMap = naverMap.apply { | ||
isNightModeEnabled = true | ||
mapType = NaverMap.MapType.Navi | ||
minZoom = MIN_ZOOM | ||
|
||
with(uiSettings) { | ||
isZoomControlEnabled = false | ||
|
@@ -74,7 +77,7 @@ class MapFragment : BindingFragment<FragmentMapBinding>(R.layout.fragment_map), | |
} | ||
} | ||
|
||
makeMarkers() | ||
mapViewModel.getPinListWithoutFilter() | ||
setLocationTrackingMode() | ||
} | ||
|
||
|
@@ -99,14 +102,11 @@ class MapFragment : BindingFragment<FragmentMapBinding>(R.layout.fragment_map), | |
chipMapCategoryOthers.setChipCategoryType(CategoryType.OTHERS) | ||
cardMap.listener = object : OnPingleCardClickListener { | ||
override fun onPingleCardChatBtnClickListener() { | ||
startActivity(navigateToWebView(mapViewModel.dummyPingle.chatLink)) | ||
// TODO 선택된 마커로 웹뷰 연결 | ||
} | ||
|
||
override fun onPingleCardParticipateBtnClickListener() { | ||
when (mapViewModel.dummyPingle.isParticipating) { | ||
true -> showMapCancelModalDialogFragment() | ||
false -> showMapModalDialogFragment() | ||
} | ||
// TODO 선택된 마커 참여 현황 여부에 따른 모달 로직 구현 | ||
} | ||
} | ||
} | ||
|
@@ -139,9 +139,20 @@ class MapFragment : BindingFragment<FragmentMapBinding>(R.layout.fragment_map), | |
} | ||
|
||
private fun collectData() { | ||
mapViewModel.category.flowWithLifecycle(lifecycle).onEach { | ||
// TODO 서버 통신 구현 시 삭제 예정 | ||
showToast(it?.name ?: "null") | ||
mapViewModel.category.flowWithLifecycle(lifecycle).onEach { category -> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋다.. 이 코드.. 쇽샥 가능하겠군요.. |
||
mapViewModel.getPinListWithoutFilter() | ||
}.launchIn(lifecycleScope) | ||
|
||
mapViewModel.markerListState.flowWithLifecycle(lifecycle).onEach { uiState -> | ||
when (uiState) { | ||
is UiState.Success -> { | ||
if (::naverMap.isInitialized) { | ||
makeMarkers(uiState.data) | ||
} | ||
} | ||
|
||
else -> Unit | ||
} | ||
}.launchIn(lifecycleScope) | ||
|
||
mapViewModel.selectedMarkerPosition.flowWithLifecycle(lifecycle) | ||
|
@@ -192,17 +203,15 @@ class MapFragment : BindingFragment<FragmentMapBinding>(R.layout.fragment_map), | |
} | ||
} | ||
|
||
private fun makeMarkers() { | ||
mapViewModel.markerList.value.mapIndexed { index, markerModel -> | ||
private fun makeMarkers(markerList: List<MarkerModel>) { | ||
mapViewModel.clearMarkerList() | ||
|
||
markerList.mapIndexed { _, markerModel -> | ||
markerModel.marker.apply { | ||
mapViewModel.addMarkerList(this) | ||
map = naverMap | ||
setOnClickListener { | ||
with(mapViewModel) { | ||
handleMarkerClick(index) | ||
// TODO 마커 상세 정보 받아오는 로직 추가 | ||
binding.cardMap.initLayout(dummyPingle) | ||
moveMapCamera(position) | ||
} | ||
// TODO 마커 클릭 이벤트 수정 | ||
return@setOnClickListener true | ||
} | ||
} | ||
|
@@ -215,22 +224,14 @@ class MapFragment : BindingFragment<FragmentMapBinding>(R.layout.fragment_map), | |
detail = stringOf(R.string.map_cancel_modal_detail), | ||
buttonText = stringOf(R.string.map_cancel_modal_button_text), | ||
textButtonText = stringOf(R.string.map_cancel_modal_text_button_text), | ||
clickBtn = { mapViewModel.cancelPingle() }, | ||
clickBtn = { }, | ||
clickTextBtn = { }, | ||
onDialogClosed = { binding.cardMap.initLayout(mapViewModel.dummyPingle) } | ||
onDialogClosed = { } | ||
).show(childFragmentManager, MAP_CANCEL_MODAL) | ||
} | ||
|
||
private fun showMapModalDialogFragment() { | ||
with(mapViewModel.dummyPingle) { | ||
MapModalDialogFragment( | ||
category = CategoryType.fromString(categoryName = category), | ||
name = name, | ||
ownerName = ownerName, | ||
clickBtn = { mapViewModel.joinPingle() }, | ||
onDialogClosed = { binding.cardMap.initLayout(mapViewModel.dummyPingle) } | ||
).show(childFragmentManager, MAP_MODAL) | ||
} | ||
// TODO 취소 모달 구현 | ||
} | ||
|
||
companion object { | ||
|
@@ -252,8 +253,13 @@ class MapFragment : BindingFragment<FragmentMapBinding>(R.layout.fragment_map), | |
val OVERLAY_IMAGE_PIN_OTHERS_DEFAULT = | ||
OverlayImage.fromResource(R.drawable.ic_pin_others_default) | ||
val OVERLAY_IMAGE_PIN_PLAY_ACTIVE = OverlayImage.fromResource(R.drawable.ic_pin_play_active) | ||
val OVERLAY_IMAGE_PIN_STUDY_ACTIVE = OverlayImage.fromResource(R.drawable.ic_pin_study_active) | ||
val OVERLAY_IMAGE_PIN_MULTI_ACTIVE = OverlayImage.fromResource(R.drawable.ic_pin_multi_active) | ||
val OVERLAY_IMAGE_PIN_OTHERS_ACTIVE = OverlayImage.fromResource(R.drawable.ic_pin_others_active) | ||
val OVERLAY_IMAGE_PIN_STUDY_ACTIVE = | ||
OverlayImage.fromResource(R.drawable.ic_pin_study_active) | ||
val OVERLAY_IMAGE_PIN_MULTI_ACTIVE = | ||
OverlayImage.fromResource(R.drawable.ic_pin_multi_active) | ||
val OVERLAY_IMAGE_PIN_OTHERS_ACTIVE = | ||
OverlayImage.fromResource(R.drawable.ic_pin_others_active) | ||
|
||
private const val MIN_ZOOM = 5.5 | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아 애초에 여기서부터 List로 선언을 해줘야 하는 군여,, Service에서만 선언하니까 오류가 난거였티비..~