Skip to content

Commit

Permalink
Merge pull request #95 from TeamPINGLE/feat-pin-list-api-connection
Browse files Browse the repository at this point in the history
[feat] 핀 목록 (좌표 필터링x) API 연동
  • Loading branch information
jihyunniiii authored Jan 10, 2024
2 parents c91feb1 + 16862e1 commit 8ba5827
Show file tree
Hide file tree
Showing 16 changed files with 243 additions and 129 deletions.
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(
private val mapService: MapService
) : MapRemoteDataSource {
override suspend fun getPinListWithoutFiltering(
teamId: Long,
category: String?
): BaseResponse<List<ResponsePinListDto>> =
mapService.getPinListWithoutFiltering(teamId = teamId, category = category)
}
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()
}
}
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 {
const val VERSION = "v1"
const val TEAMS = "teams"
const val PINS = "pins"
const val TEAM_ID = "teamId"
const val CATEGORY = "category"
}
}
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
}
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
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
@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>> =
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
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 ->
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

0 comments on commit 8ba5827

Please sign in to comment.