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] 핀 목록 (좌표 필터링x) API 연동 #95

Merged
merged 4 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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.ResponsePinListDto
import org.sopt.pingle.util.base.BaseResponse

interface MapRemoteDataSource {
suspend fun getPinListWithoutFiltering(teamId: Long, category: String?): BaseResponse<List<ResponsePinListDto>>
Copy link
Collaborator

Choose a reason for hiding this comment

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

아 애초에 여기서부터 List로 선언을 해줘야 하는 군여,, Service에서만 선언하니까 오류가 난거였티비..~

}
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(
Copy link
Collaborator

Choose a reason for hiding this comment

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

@Inject 어노테이션은 디펜던시에 힐트 설정 안 하면 못 쓰는 앤가요?

Copy link
Member

Choose a reason for hiding this comment

The 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)
Copy link
Collaborator

Choose a reason for hiding this comment

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

데이터소스에서 함수 추상화하고 데이터소스Impl에서 서비스에 접근해서 통신한 값들 불러오는 함수를 호출하는 거까지 이해 완...
갑자기 궁금한게 생겼는데 서비스는 왜 인터페이스인걸가요? 얘네는 오버라이드 해야되는 함수를 구현하는 것도 아닌데.. 흠냐

Copy link
Member

Choose a reason for hiding this comment

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

구현해야할 코드가 없고 서버통신에서 필요한 부분이 어노테이션과 매개변수에 있어서 그런게 아닐까요..?!

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class AuthInterceptor @Inject constructor() : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val authRequest =
originalRequest.newBuilder().addHeader(ACCESS_TOKEN, BuildConfig.ACCESS_TOKEN).build()
originalRequest.newBuilder().addHeader(AUTHORIZATION, BuildConfig.ACCESS_TOKEN).build()
val response = chain.proceed(authRequest)

when (response.code) {
Expand All @@ -21,6 +21,6 @@ class AuthInterceptor @Inject constructor() : Interceptor {
}

companion object {
const val ACCESS_TOKEN = "accessToken"
const val AUTHORIZATION = "Authorization"
}
}
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
Copy link
Member

Choose a reason for hiding this comment

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

이런식으로 하는거 몰랐었는데 코드짜면서 막힐 때 보고 배웠습니닷!

}
emit(result.getOrThrow())
}
}
23 changes: 23 additions & 0 deletions app/src/main/java/org/sopt/pingle/data/service/MapService.kt
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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
Copy link
Member

Choose a reason for hiding this comment

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

이 부분 상수화를 모든 Service 파일마다 해야하는건가요!?

Copy link
Member

Choose a reason for hiding this comment

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

더 궁금한점이 나중에 url이 바뀔 가능성과 가독성을 위한거라면 상수화한 파일만 따로만들어서 서버통신 부분에서 공통되게 사용해야하는게 맞는 방법이라고 생각이 들어요!

}
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 @@ -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)
Expand All @@ -20,4 +22,8 @@ abstract class DataSourceModule {
@Binds
@Singleton
abstract fun bindsDummyRemoteDataSource(dummyRemoteDataSourceImpl: DummyRemoteDataSourceImpl): DummyRemoteDataSource

@Binds
@Singleton
abstract fun bindsMapRemoteDataSource(mapRemoteDataSourceImpl: MapRemoteDataSourceImpl): MapRemoteDataSource
Copy link
Collaborator

Choose a reason for hiding this comment

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

이 부분이 제일 이해가 안되는데................................. 힐트 부분인건가요? 대충 뭘 많이 모아둔.. 데이터소스를 모아둔 부분인 건 알겠는데요.........

}
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 @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

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

@singleton 객체의 인스턴스가 1개만 생성되는 싱글톤 패턴을 구현하기 위해 존재하는 어노테이션인가요? 이 어노테이션의 의미가 궁금합니다링구

Copy link
Collaborator

Choose a reason for hiding this comment

The 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
}
7 changes: 7 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 @@ -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)
Expand All @@ -16,4 +18,9 @@ object ServiceModule {
@Singleton
fun providesDummyService(@Pingle retrofit: Retrofit): DummyService =
retrofit.create(DummyService::class.java)

@Provides
Copy link
Collaborator

Choose a reason for hiding this comment

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

@BINDS@provides의 차이가 무엇인가욤?

Copy link
Collaborator

Choose a reason for hiding this comment

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

혼자 공부완ㅋㅅㅋ

@Singleton
fun providesMapService(@Pingle retrofit: Retrofit): MapService =
retrofit.create(MapService::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 @@ -6,7 +6,9 @@ import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
import org.sopt.pingle.domain.repository.DummyRepository
import org.sopt.pingle.domain.repository.MapRepository
import org.sopt.pingle.domain.usecase.GetDummyUserListUseCase
import org.sopt.pingle.domain.usecase.GetPinListWithoutFilteringUseCase
import org.sopt.pingle.domain.usecase.SetDummyDataUseCase

@Module
Expand All @@ -21,4 +23,9 @@ class UseCaseModule {
@Singleton
fun providesGetDummyUserListUseCase(dummyRepository: DummyRepository): GetDummyUserListUseCase =
GetDummyUserListUseCase(dummyRepository)

@Provides
@Singleton
fun providesGetPinListWithoutFilteringUseCase(mapRepository: MapRepository): GetPinListWithoutFilteringUseCase =
GetPinListWithoutFilteringUseCase(mapRepository = mapRepository)
}
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>> =
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
Expand Up @@ -10,7 +10,7 @@ import org.sopt.pingle.presentation.type.CategoryType
fun PinEntity.toMarkerModel(): MarkerModel {
val markerModel = MarkerModel(
Marker().apply {
position = LatLng(y, x)
position = LatLng(x, y)
isHideCollidedMarkers = true
icon = CategoryType.fromString(category).toMarkerIcon(false)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
Expand Down Expand Up @@ -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
Expand All @@ -74,7 +77,7 @@ class MapFragment : BindingFragment<FragmentMapBinding>(R.layout.fragment_map),
}
}

makeMarkers()
mapViewModel.getPinListWithoutFilter()
setLocationTrackingMode()
}

Expand All @@ -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 선택된 마커 참여 현황 여부에 따른 모달 로직 구현
}
}
}
Expand Down Expand Up @@ -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 ->
Copy link
Collaborator

Choose a reason for hiding this comment

The 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)
Expand Down Expand Up @@ -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
}
}
Expand All @@ -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 {
Expand All @@ -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
}
}
Loading
Loading