Skip to content
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
Expand Up @@ -131,6 +131,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationController {
com.project200.undabang.feature.matching.R.id.matchingProfileFragment,
com.project200.undabang.feature.matching.R.id.exercisePlaceFragment,
com.project200.undabang.feature.matching.R.id.exercisePlaceSearchFragment,
com.project200.undabang.feature.matching.R.id.exercisePlaceRegisterFragment,
// ... 필요한 다른 프래그먼트 ID들 추가 ... //
)

Expand Down
17 changes: 17 additions & 0 deletions data/src/main/java/com/project200/data/api/ApiService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.project200.data.api

import com.project200.data.dto.BaseResponse
import com.project200.data.dto.CustomTimerIdDTO
import com.project200.data.dto.EditExercisePlaceDTO
import com.project200.data.dto.ExerciseIdDto
import com.project200.data.dto.ExpectedScoreInfoDTO
import com.project200.data.dto.GetCustomTimerDetailDTO
Expand All @@ -21,6 +22,7 @@ import com.project200.data.dto.PatchCustomTimerTitleRequest
import com.project200.data.dto.PatchExerciseRequestDto
import com.project200.data.dto.PolicyGroupDTO
import com.project200.data.dto.PostCustomTimerRequest
import com.project200.data.dto.PostExercisePlaceDTO
import com.project200.data.dto.PostExerciseRequestDto
import com.project200.data.dto.PostExerciseResponseDTO
import com.project200.data.dto.PostSignUpData
Expand Down Expand Up @@ -280,4 +282,19 @@ interface ApiService {
suspend fun deleteExercisePlace(
@Path("locationId") locationId: Long,
): BaseResponse<Any?>

// 운동 장소 등록
@POST("api/v1/exercise-locations")
@AccessTokenApi
suspend fun postExercisePlace(
@Body placeInfo: PostExercisePlaceDTO,
): BaseResponse<Any?>

// 운동 장소 수정
@PUT("api/v1/exercise-locations/{locationId}")
@AccessTokenApi
suspend fun putExercisePlace(
@Path("locationId") locationId: Long,
@Body placeName: EditExercisePlaceDTO,
): BaseResponse<Any?>
}
13 changes: 13 additions & 0 deletions data/src/main/java/com/project200/data/dto/MatchingDTO.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,16 @@ data class GetExercisePlaceDTO(
data class DeleteExercisePlaceDTO(
val id: Long,
)

@JsonClass(generateAdapter = true)
data class PostExercisePlaceDTO(
val name: String,
val address: String,
val latitude: Double,
val longitude: Double,
)

@JsonClass(generateAdapter = true)
data class EditExercisePlaceDTO(
val exerciseLocationName: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import com.project200.common.constants.RuleConstants.ZOOM_LEVEL
import com.project200.common.di.IoDispatcher
import com.project200.common.utils.DefaultPrefs
import com.project200.data.api.ApiService
import com.project200.data.dto.EditExercisePlaceDTO
import com.project200.data.dto.GetExerciseCountByRangeDTO
import com.project200.data.mapper.toDTO
import com.project200.data.mapper.toModel
import com.project200.data.utils.apiCallBuilder
import com.project200.domain.model.BaseResult
Expand Down Expand Up @@ -257,7 +259,23 @@ class MatchingRepositoryImpl
)*/
}

companion object {
override suspend fun addExercisePlace(placeInfo: ExercisePlace): BaseResult<Unit> {
return apiCallBuilder(
ioDispatcher = ioDispatcher,
apiCall = { apiService.postExercisePlace(placeInfo.toDTO())},
mapper = { Unit }
)
}

override suspend fun editExercisePlace(placeInfo: ExercisePlace): BaseResult<Unit> {
return apiCallBuilder(
ioDispatcher = ioDispatcher,
apiCall = { apiService.putExercisePlace(placeInfo.id, EditExercisePlaceDTO(placeInfo.name))},
mapper = { Unit }
)
}

companion object {
private const val KEY_LAST_LAT = "key_last_lat"
private const val KEY_LAST_LON = "key_last_lon"
private const val KEY_LAST_ZOOM = "key_last_zoom"
Expand Down
10 changes: 10 additions & 0 deletions data/src/main/java/com/project200/data/mapper/MatchingMapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.project200.data.dto.GetExercisePlaceDTO
import com.project200.data.dto.GetMatchingMembersDto
import com.project200.data.dto.GetMatchingProfileDTO
import com.project200.data.dto.LocationDto
import com.project200.data.dto.PostExercisePlaceDTO
import com.project200.domain.model.ExercisePlace
import com.project200.domain.model.Location
import com.project200.domain.model.MatchingMember
Expand Down Expand Up @@ -62,3 +63,12 @@ fun GetExercisePlaceDTO.toModel(): ExercisePlace {
longitude = this.longitude,
)
}

fun ExercisePlace.toDTO(): PostExercisePlaceDTO {
return PostExercisePlaceDTO(
name = this.name,
address = this.address,
latitude = this.latitude,
longitude = this.longitude,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.project200.domain.repository
import com.project200.domain.model.BaseResult
import com.project200.domain.model.ExerciseCount
import com.project200.domain.model.ExercisePlace
import com.project200.domain.model.KakaoPlaceInfo
import com.project200.domain.model.MapPosition
import com.project200.domain.model.MatchingMember
import com.project200.domain.model.MatchingMemberProfile
Expand All @@ -24,4 +25,8 @@ interface MatchingRepository {
suspend fun getExercisePlaces(): BaseResult<List<ExercisePlace>>
// 운동 장소 삭제
suspend fun deleteExercisePlace(placeId: Long): BaseResult<Unit>
// 운동 장소 등록
suspend fun addExercisePlace(placeInfo: ExercisePlace): BaseResult<Unit>
// 운동 장소 수정
suspend fun editExercisePlace(placeInfo: ExercisePlace): BaseResult<Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.project200.domain.usecase

import com.project200.domain.model.BaseResult
import com.project200.domain.model.ExercisePlace
import com.project200.domain.repository.MatchingRepository
import javax.inject.Inject

class EditExercisePlaceUseCase @Inject constructor(
private val matchingRepository: MatchingRepository
) {
suspend operator fun invoke(placeInfo: ExercisePlace): BaseResult<Unit> {
return matchingRepository.editExercisePlace(placeInfo)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.project200.domain.usecase

import com.project200.domain.model.BaseResult
import com.project200.domain.model.ExercisePlace
import com.project200.domain.repository.MatchingRepository
import javax.inject.Inject

class RegisterExercisePlaceUseCase @Inject constructor(
private val matchingRepository: MatchingRepository
) {
suspend operator fun invoke(placeInfo: ExercisePlace): BaseResult<Unit> {
return matchingRepository.addExercisePlace(placeInfo)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.project200.feature.matching.place

import android.content.Context
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.project200.domain.model.BaseResult
import com.project200.presentation.base.BindingFragment
import com.project200.undabang.feature.matching.R
import com.project200.undabang.feature.matching.databinding.FragmentExercisePlaceRegisterBinding
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint // Hilt 어노테이션 추가
class ExercisePlaceRegisterFragment: BindingFragment<FragmentExercisePlaceRegisterBinding> (R.layout.fragment_exercise_place_register) {
private val viewModel: ExercisePlaceRegisterViewModel by viewModels()
private val args: ExercisePlaceRegisterFragmentArgs by navArgs()

override fun getViewBinding(view: View): FragmentExercisePlaceRegisterBinding {
return FragmentExercisePlaceRegisterBinding.bind(view)
}

override fun setupViews() {
binding.baseToolbar.apply {
setTitle(getString(R.string.exercise_place_register_title))
showBackButton(true) { findNavController().navigateUp() }
}
binding.placeAddressTv.text = args.address

viewModel.initializePlaceInfo(
id = args.placeId,
placeName = args.name,
placeAddress = args.address,
latitude= args.latitude.toDouble(),
longitude = args.longitude.toDouble()
)
binding.placeNameEt.setText(args.name)
setupListeners()
}

private fun setupListeners() {
binding.placeNameEt.doAfterTextChanged { text ->
viewModel.onPlaceNameChanged(text.toString())
}

binding.registerBtn.setOnClickListener {
if (binding.placeNameEt.text.toString().isEmpty()) {
Toast.makeText(requireContext(), R.string.empty_place_name, Toast.LENGTH_SHORT).show()
}
viewModel.confirmExercisePlace()
}
}

override fun setupObservers() {
viewModel.customPlaceName.observe(viewLifecycleOwner) { name ->
binding.placeNameTv.text = if(name.isBlank()) getString(R.string.place_name_hint) else name
}

viewModel.registrationResult.observe(viewLifecycleOwner) { result ->
when (result) {
is BaseResult.Success -> {
Toast.makeText(requireContext(), getString(R.string.success_register_place), Toast.LENGTH_SHORT).show()
findNavController().navigateUp()
// TODO: 진입 경로에 따라 완료 후 화면 이동 경로 변경
}
is BaseResult.Error -> {
Toast.makeText(requireContext(), R.string.error_fail_to_register_place, Toast.LENGTH_SHORT).show()
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.project200.feature.matching.place

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.project200.domain.model.BaseResult
import com.project200.domain.model.ExercisePlace
import com.project200.domain.usecase.EditExercisePlaceUseCase
import com.project200.domain.usecase.RegisterExercisePlaceUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class ExercisePlaceRegisterViewModel @Inject constructor(
private val registerExercisePlaceUseCase: RegisterExercisePlaceUseCase,
private val editExercisePlace: EditExercisePlaceUseCase
) : ViewModel() {
// 원본 장소 정보를 저장 (Fragment에서 arguments로 전달받음)
private val _originalPlaceInfo = MutableLiveData<ExercisePlace>()

private val _customPlaceName = MutableLiveData<String>()
val customPlaceName: LiveData<String> = _customPlaceName

private val _registrationResult = MutableLiveData<BaseResult<Unit>>()
val registrationResult: LiveData<BaseResult<Unit>> = _registrationResult

private val _editResult = MutableLiveData<BaseResult<Unit>>()
val editResult: LiveData<BaseResult<Unit>> = _editResult

/**
* Fragment에서 받은 arguments로 ViewModel 초기화
*/
fun initializePlaceInfo(id: Long, placeName: String, placeAddress: String, latitude: Double, longitude: Double) {
val placeInfo = ExercisePlace(
id = id,
name = placeName,
address = placeAddress,
latitude = latitude,
longitude = longitude
)
_originalPlaceInfo.value = placeInfo
_customPlaceName.value = placeInfo.name
}

/**
* EditText의 텍스트가 변경될 때마다 호출
*/
fun onPlaceNameChanged(newName: String) {
_customPlaceName.value = newName
}

/**
* 주소 등록
*/
fun confirmExercisePlace() {
viewModelScope.launch {
val customName = _customPlaceName.value
if (customName.isNullOrBlank()) return@launch

val originalInfo = _originalPlaceInfo.value ?: return@launch

if(originalInfo.id != DEFAULT_PLACE_ID) { // 기존 장소 수정
val result = editExercisePlace(originalInfo.copy(name = customName))
_editResult.value = result
} else { // 신규 장소 등록
val result = registerExercisePlaceUseCase(originalInfo.copy(name = customName))
_registrationResult.value = result
}
}
}

companion object {
const val DEFAULT_PLACE_ID = -1L
}
}
Loading