diff --git a/android/app/src/main/java/com/now/naaga/data/remote/retrofit/service/AdventureService.kt b/android/app/src/main/java/com/now/naaga/data/remote/retrofit/service/AdventureService.kt index 96ac30141..b21316daf 100644 --- a/android/app/src/main/java/com/now/naaga/data/remote/retrofit/service/AdventureService.kt +++ b/android/app/src/main/java/com/now/naaga/data/remote/retrofit/service/AdventureService.kt @@ -6,7 +6,7 @@ import com.now.naaga.data.remote.dto.AdventureStatusDto import com.now.naaga.data.remote.dto.CoordinateDto import com.now.naaga.data.remote.dto.FinishGameDto import com.now.naaga.data.remote.dto.HintDto -import retrofit2.Call +import retrofit2.Response import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.PATCH @@ -16,49 +16,49 @@ import retrofit2.http.Query interface AdventureService { @GET("/games") - fun getMyGames(): Call> + suspend fun getMyGames(): Response> @GET("/games/{gameId}") - fun getGame( + suspend fun getGame( @Path("gameId") gameId: Long, - ): Call + ): Response @GET("/games") - fun getGamesByStatus( + suspend fun getGamesByStatus( @Query("status") status: String, - ): Call> + ): Response> @POST("/games") - fun beginGame( + suspend fun beginGame( @Body coordinateDto: CoordinateDto, - ): Call + ): Response @PATCH("/games/{gameId}") - fun endGame( + suspend fun endGame( @Path("gameId") gameId: Long, @Body finishGameDto: FinishGameDto, - ): Call + ): Response @GET("/games/{gameId}/result") - fun getGameResult( + suspend fun getGameResult( @Path("gameId") gameId: Long, - ): Call + ): Response @GET("/games/results") - fun getMyGameResults( + suspend fun getMyGameResults( @Query("sort-by") sortBy: String, @Query("order") order: String, - ): Call> + ): Response> @GET("/games/{gameId}/hints/{hintId}") - fun getHint( + suspend fun getHint( @Path("gameId") gameId: Long, @Path("hintId") hingId: Long, - ): Call + ): Response @POST("/games/{gameId}/hints") - fun requestHint( + suspend fun requestHint( @Path("gameId") gameId: Long, @Body coordinateDto: CoordinateDto, - ): Call + ): Response } diff --git a/android/app/src/main/java/com/now/naaga/data/repository/DefaultAdventureRepository.kt b/android/app/src/main/java/com/now/naaga/data/repository/DefaultAdventureRepository.kt index c4546626a..75bec02ee 100644 --- a/android/app/src/main/java/com/now/naaga/data/repository/DefaultAdventureRepository.kt +++ b/android/app/src/main/java/com/now/naaga/data/repository/DefaultAdventureRepository.kt @@ -11,127 +11,61 @@ import com.now.domain.model.SortType import com.now.domain.repository.AdventureRepository import com.now.naaga.data.mapper.toDomain import com.now.naaga.data.mapper.toDto -import com.now.naaga.data.remote.dto.AdventureDto import com.now.naaga.data.remote.dto.FinishGameDto import com.now.naaga.data.remote.retrofit.ServicePool -import com.now.naaga.data.remote.retrofit.fetchResponse +import com.now.naaga.util.getValueOrThrow class DefaultAdventureRepository : AdventureRepository { - override fun fetchMyAdventures(callback: (Result>) -> Unit) { - val call = ServicePool.adventureService.getMyGames() - call.fetchResponse( - onSuccess = { adventureDtos: List -> - callback(Result.success(adventureDtos.map { it.toDomain() })) - }, - onFailure = { - callback(Result.failure(it)) - }, - ) + override suspend fun fetchMyAdventures(): List { + val response = ServicePool.adventureService.getMyGames().getValueOrThrow() + return response.map { it.toDomain() } } - override fun fetchAdventure(adventureId: Long, callback: (Result) -> Unit) { - val call = ServicePool.adventureService.getGame(adventureId) - call.fetchResponse( - onSuccess = { adventureDto -> - callback(Result.success(adventureDto.toDomain())) - }, - onFailure = { - callback(Result.failure(it)) - }, - ) + override suspend fun fetchAdventure(adventureId: Long): Adventure { + val response = ServicePool.adventureService.getGame(adventureId).getValueOrThrow() + return response.toDomain() } - override fun fetchAdventureByStatus(status: AdventureStatus, callback: (Result>) -> Unit) { - val call = ServicePool.adventureService.getGamesByStatus(status.name) - call.fetchResponse( - onSuccess = { adventureDtos: List -> - callback(Result.success(adventureDtos.map { it.toDomain() })) - }, - onFailure = { - callback(Result.failure(it)) - }, - ) + override suspend fun fetchAdventureByStatus(status: AdventureStatus): List { + val response = ServicePool.adventureService.getGamesByStatus(status.name).getValueOrThrow() + return response.map { it.toDomain() } } - override fun beginAdventure(coordinate: Coordinate, callback: (Result) -> Unit) { - val call = ServicePool.adventureService.beginGame(coordinate.toDto()) - call.fetchResponse( - onSuccess = { adventureDto -> - callback(Result.success(adventureDto.toDomain())) - }, - onFailure = { - callback(Result.failure(it)) - }, - ) + override suspend fun beginAdventure(coordinate: Coordinate): Adventure { + val response = ServicePool.adventureService.beginGame(coordinate.toDto()).getValueOrThrow() + return response.toDomain() } - override fun endGame( + override suspend fun endGame( adventureId: Long, endType: AdventureEndType, coordinate: Coordinate, - callback: (Result) -> Unit, - ) { + ): AdventureStatus { val finishGameDto = FinishGameDto(endType.name, coordinate.toDto()) - val call = ServicePool.adventureService.endGame(adventureId, finishGameDto) - call.fetchResponse( - onSuccess = { adventureStatusDto -> - callback(Result.success(AdventureStatus.getStatus(adventureStatusDto.gameStatus))) - }, - onFailure = { - callback(Result.failure(it)) - }, - ) + val response = ServicePool.adventureService.endGame(adventureId, finishGameDto).getValueOrThrow() + return AdventureStatus.getStatus(response.gameStatus) } - override fun fetchAdventureResult(adventureId: Long, callback: (Result) -> Unit) { - val call = ServicePool.adventureService.getGameResult(adventureId) - call.fetchResponse( - onSuccess = { adventureResultDto -> - callback(Result.success(adventureResultDto.toDomain())) - }, - onFailure = { - callback(Result.failure(it)) - }, - ) + override suspend fun fetchAdventureResult(adventureId: Long): AdventureResult { + val response = ServicePool.adventureService.getGameResult(adventureId).getValueOrThrow() + return response.toDomain() } - override fun fetchMyAdventureResults( + override suspend fun fetchMyAdventureResults( sortBy: SortType, order: OrderType, - callback: (Result>) -> Unit, - ) { - val call = ServicePool.adventureService.getMyGameResults(sortBy.name, order.name) - call.fetchResponse( - onSuccess = { adventureResultDtos -> - callback(Result.success(adventureResultDtos.map { it.toDomain() })) - }, - onFailure = { - callback(Result.failure(it)) - }, - ) + ): List { + val response = ServicePool.adventureService.getMyGameResults(sortBy.name, order.name).getValueOrThrow() + return response.map { it.toDomain() } } - override fun fetchHint(adventureId: Long, hintId: Long, callback: (Result) -> Unit) { - val call = ServicePool.adventureService.getHint(adventureId, hintId) - call.fetchResponse( - onSuccess = { hintDto -> - callback(Result.success(hintDto.toDomain())) - }, - onFailure = { - callback(Result.failure(it)) - }, - ) + override suspend fun fetchHint(adventureId: Long, hintId: Long): Hint { + val response = ServicePool.adventureService.getHint(adventureId, hintId).getValueOrThrow() + return response.toDomain() } - override fun makeHint(adventureId: Long, coordinate: Coordinate, callback: (Result) -> Unit) { - val call = ServicePool.adventureService.requestHint(adventureId, coordinate.toDto()) - call.fetchResponse( - onSuccess = { hintDto -> - callback(Result.success(hintDto.toDomain())) - }, - onFailure = { - callback(Result.failure(it)) - }, - ) + override suspend fun makeHint(adventureId: Long, coordinate: Coordinate): Hint { + val response = ServicePool.adventureService.requestHint(adventureId, coordinate.toDto()).getValueOrThrow() + return response.toDomain() } } diff --git a/android/app/src/main/java/com/now/naaga/presentation/adventurehistory/AdventureHistoryViewModel.kt b/android/app/src/main/java/com/now/naaga/presentation/adventurehistory/AdventureHistoryViewModel.kt index 5fceb76c8..ef95d160f 100644 --- a/android/app/src/main/java/com/now/naaga/presentation/adventurehistory/AdventureHistoryViewModel.kt +++ b/android/app/src/main/java/com/now/naaga/presentation/adventurehistory/AdventureHistoryViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope import com.now.domain.model.AdventureResult import com.now.domain.model.OrderType import com.now.domain.model.SortType @@ -11,6 +12,7 @@ import com.now.domain.repository.AdventureRepository import com.now.naaga.data.repository.DefaultAdventureRepository import com.now.naaga.data.throwable.DataThrowable import com.now.naaga.data.throwable.DataThrowable.PlayerThrowable +import kotlinx.coroutines.launch class AdventureHistoryViewModel(private val adventureRepository: AdventureRepository) : ViewModel() { private val _adventureResults = MutableLiveData>() @@ -20,16 +22,23 @@ class AdventureHistoryViewModel(private val adventureRepository: AdventureReposi val errorMessage: LiveData = _errorMessage fun fetchHistories() { - adventureRepository.fetchMyAdventureResults(SortType.TIME, OrderType.DESCENDING) { result -> - result - .onSuccess { _adventureResults.value = it } - .onFailure { setErrorMessage(it as DataThrowable) } + viewModelScope.launch { + runCatching { + adventureRepository.fetchMyAdventureResults(SortType.TIME, OrderType.DESCENDING) + }.onSuccess { results: List -> + _adventureResults.value = results + }.onFailure { + setErrorMessage(it as DataThrowable) + } } } private fun setErrorMessage(throwable: DataThrowable) { when (throwable) { - is PlayerThrowable -> { _errorMessage.value = throwable.message } + is PlayerThrowable -> { + _errorMessage.value = throwable.message + } + else -> {} } } diff --git a/android/app/src/main/java/com/now/naaga/presentation/adventureresult/AdventureResultViewModel.kt b/android/app/src/main/java/com/now/naaga/presentation/adventureresult/AdventureResultViewModel.kt index 494d37bf1..e25b14403 100644 --- a/android/app/src/main/java/com/now/naaga/presentation/adventureresult/AdventureResultViewModel.kt +++ b/android/app/src/main/java/com/now/naaga/presentation/adventureresult/AdventureResultViewModel.kt @@ -29,14 +29,15 @@ class AdventureResultViewModel( val throwable: LiveData = _throwable fun fetchGameResult(adventureId: Long) { - adventureRepository.fetchAdventureResult( - adventureId, - callback = { result -> - result - .onSuccess { adventureResult -> _adventureResult.value = adventureResult } - .onFailure { setErrorMessage(it as DataThrowable) } - }, - ) + viewModelScope.launch { + runCatching { + adventureRepository.fetchAdventureResult(adventureId) + }.onSuccess { adventureResult -> + _adventureResult.value = adventureResult + }.onFailure { + setErrorMessage(it as DataThrowable) + } + } } fun fetchMyRank() { @@ -53,7 +54,10 @@ class AdventureResultViewModel( private fun setErrorMessage(throwable: DataThrowable) { when (throwable) { - is GameThrowable -> { _throwable.value = throwable } + is GameThrowable -> { + _throwable.value = throwable + } + else -> {} } } diff --git a/android/app/src/main/java/com/now/naaga/presentation/beginadventure/BeginAdventureViewModel.kt b/android/app/src/main/java/com/now/naaga/presentation/beginadventure/BeginAdventureViewModel.kt index 1a43660f1..fb4b9ae45 100644 --- a/android/app/src/main/java/com/now/naaga/presentation/beginadventure/BeginAdventureViewModel.kt +++ b/android/app/src/main/java/com/now/naaga/presentation/beginadventure/BeginAdventureViewModel.kt @@ -4,11 +4,13 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope import com.now.domain.model.Adventure import com.now.domain.model.AdventureStatus import com.now.domain.repository.AdventureRepository import com.now.naaga.data.repository.DefaultAdventureRepository import com.now.naaga.data.throwable.DataThrowable +import kotlinx.coroutines.launch class BeginAdventureViewModel(private val adventureRepository: AdventureRepository) : ViewModel() { private val _adventure = MutableLiveData() @@ -22,15 +24,15 @@ class BeginAdventureViewModel(private val adventureRepository: AdventureReposito fun fetchAdventure(adventureStatus: AdventureStatus) { _loading.value = true - adventureRepository.fetchAdventureByStatus(adventureStatus) { result -> - _loading.value = false - result - .onSuccess { - _adventure.value = it.firstOrNull() - } - .onFailure { - _error.value = it as DataThrowable - } + viewModelScope.launch { + runCatching { + adventureRepository.fetchAdventureByStatus(adventureStatus) + }.onSuccess { + _loading.value = false + _adventure.value = it.firstOrNull() + }.onFailure { + _error.value = it as DataThrowable + } } } diff --git a/android/app/src/main/java/com/now/naaga/presentation/onadventure/OnAdventureViewModel.kt b/android/app/src/main/java/com/now/naaga/presentation/onadventure/OnAdventureViewModel.kt index 66ce642ed..05b0751d2 100644 --- a/android/app/src/main/java/com/now/naaga/presentation/onadventure/OnAdventureViewModel.kt +++ b/android/app/src/main/java/com/now/naaga/presentation/onadventure/OnAdventureViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.map +import androidx.lifecycle.viewModelScope import com.now.domain.model.Adventure import com.now.domain.model.AdventureEndType import com.now.domain.model.AdventureStatus @@ -17,6 +18,7 @@ import com.now.naaga.data.throwable.DataThrowable import com.now.naaga.data.throwable.DataThrowable.Companion.hintThrowable import com.now.naaga.data.throwable.DataThrowable.GameThrowable import com.now.naaga.data.throwable.DataThrowable.UniversalThrowable +import kotlinx.coroutines.launch class OnAdventureViewModel(private val adventureRepository: AdventureRepository) : ViewModel() { private val _adventure = MutableLiveData() @@ -41,22 +43,30 @@ class OnAdventureViewModel(private val adventureRepository: AdventureRepository) } fun beginAdventure(currentCoordinate: Coordinate) { - adventureRepository.beginAdventure(currentCoordinate) { result: Result -> - result - .onSuccess { setAdventure(it) } - .onFailure { setError(it as DataThrowable) } + viewModelScope.launch { + runCatching { + adventureRepository.beginAdventure(currentCoordinate) + }.onSuccess { + setAdventure(it) + }.onFailure { + setError(it as DataThrowable) + } } } fun giveUpAdventure() { - adventureRepository.endGame( - adventureId = adventure.value?.id ?: return, - endType = AdventureEndType.GIVE_UP, - coordinate = myCoordinate.value ?: return, - ) { result: Result -> - result - .onSuccess { _adventure.value = adventure.value?.copy(adventureStatus = it) } - .onFailure { setError(it as DataThrowable) } + viewModelScope.launch { + runCatching { + adventureRepository.endGame( + adventureId = adventure.value?.id ?: throw IllegalStateException(ADVENTURE_IS_NULL), + endType = AdventureEndType.GIVE_UP, + coordinate = myCoordinate.value ?: throw IllegalStateException(MY_COORDINATE_IS_NULL), + ) + }.onSuccess { status: AdventureStatus -> + _adventure.value = adventure.value?.copy(adventureStatus = status) + }.onFailure { + setError(it as DataThrowable) + } } } @@ -65,16 +75,18 @@ class OnAdventureViewModel(private val adventureRepository: AdventureRepository) setError(hintThrowable) return } - adventureRepository.makeHint( - adventureId = adventure.value?.id ?: return, - coordinate = myCoordinate.value ?: return, - ) { result: Result -> - result - .onSuccess { hint -> - _adventure.value = adventure.value?.copy(hints = ((adventure.value?.hints ?: listOf()) + hint)) - _lastHint.value = hint - } - .onFailure { setError(it as DataThrowable) } + viewModelScope.launch { + runCatching { + adventureRepository.makeHint( + adventureId = adventure.value?.id ?: throw IllegalStateException(ADVENTURE_IS_NULL), + coordinate = myCoordinate.value ?: throw IllegalStateException(MY_COORDINATE_IS_NULL), + ) + }.onSuccess { hint: Hint -> + _adventure.value = adventure.value?.copy(hints = ((adventure.value?.hints ?: listOf()) + hint)) + _lastHint.value = hint + }.onFailure { + setError(it as DataThrowable) + } } } @@ -88,25 +100,25 @@ class OnAdventureViewModel(private val adventureRepository: AdventureRepository) } fun endAdventure() { - adventureRepository.endGame( - adventureId = adventure.value?.id ?: return, - endType = AdventureEndType.ARRIVED, - coordinate = myCoordinate.value ?: return, - ) { result: Result -> - result - .onSuccess { _adventure.value = adventure.value?.copy(adventureStatus = it) } - .onFailure { - when ((it as DataThrowable).code) { - NOT_ARRIVED -> { - val currentRemainingTryCount = adventure.value?.remainingTryCount ?: return@onFailure - _adventure.value = adventure.value?.copy(remainingTryCount = currentRemainingTryCount - 1) - } - - TRY_COUNT_OVER -> - _adventure.value = adventure.value?.copy(adventureStatus = AdventureStatus.DONE) + viewModelScope.launch { + runCatching { + adventureRepository.endGame( + adventureId = adventure.value?.id ?: throw IllegalStateException(ADVENTURE_IS_NULL), + endType = AdventureEndType.ARRIVED, + coordinate = myCoordinate.value ?: throw IllegalStateException(MY_COORDINATE_IS_NULL), + ) + }.onSuccess { + _adventure.value = adventure.value?.copy(adventureStatus = it) + }.onFailure { + when ((it as DataThrowable).code) { + TRY_COUNT_OVER -> _adventure.value = adventure.value?.copy(adventureStatus = AdventureStatus.DONE) + NOT_ARRIVED -> { + val currentRemainingTryCount = adventure.value?.remainingTryCount ?: return@onFailure + _adventure.value = adventure.value?.copy(remainingTryCount = currentRemainingTryCount - 1) } - setError(it) } + setError(it) + } } } @@ -123,6 +135,9 @@ class OnAdventureViewModel(private val adventureRepository: AdventureRepository) const val NO_DESTINATION = 406 const val NOT_ARRIVED = 415 const val TRY_COUNT_OVER = 416 + private const val ERROR_PREFIX = "[ERROR] OnAdventureViewModel:" + private const val ADVENTURE_IS_NULL = "$ERROR_PREFIX adventure가 널입니다." + private const val MY_COORDINATE_IS_NULL = "$ERROR_PREFIX myCoordinate가 널입니다." val Factory = ViewModelFactory(DefaultAdventureRepository()) class ViewModelFactory(private val adventureRepository: AdventureRepository) : ViewModelProvider.Factory { diff --git a/android/app/src/main/java/com/now/naaga/presentation/splash/SplashViewModel.kt b/android/app/src/main/java/com/now/naaga/presentation/splash/SplashViewModel.kt index 37d240952..5d00ca994 100644 --- a/android/app/src/main/java/com/now/naaga/presentation/splash/SplashViewModel.kt +++ b/android/app/src/main/java/com/now/naaga/presentation/splash/SplashViewModel.kt @@ -4,10 +4,12 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope import com.now.domain.model.Adventure import com.now.domain.model.AdventureStatus import com.now.domain.repository.AdventureRepository import com.now.naaga.data.repository.DefaultAdventureRepository +import kotlinx.coroutines.launch class SplashViewModel(private val adventureRepository: AdventureRepository) : ViewModel() { private val _adventure = MutableLiveData() @@ -17,10 +19,14 @@ class SplashViewModel(private val adventureRepository: AdventureRepository) : Vi val adventureStatus: LiveData = _adventureStatus fun fetchInProgressAdventure() { - adventureRepository.fetchAdventureByStatus(AdventureStatus.IN_PROGRESS) { result: Result> -> - result - .onSuccess { fetchAdventure(it) } - .onFailure { _adventureStatus.value = AdventureStatus.NONE } + viewModelScope.launch { + runCatching { + adventureRepository.fetchAdventureByStatus(AdventureStatus.IN_PROGRESS) + }.onSuccess { + fetchAdventure(it) + }.onFailure { + _adventureStatus.value = AdventureStatus.NONE + } } } diff --git a/android/domain/src/main/java/com/now/domain/repository/AdventureRepository.kt b/android/domain/src/main/java/com/now/domain/repository/AdventureRepository.kt index c7c38f1f9..9ce2746c1 100644 --- a/android/domain/src/main/java/com/now/domain/repository/AdventureRepository.kt +++ b/android/domain/src/main/java/com/now/domain/repository/AdventureRepository.kt @@ -10,38 +10,31 @@ import com.now.domain.model.OrderType import com.now.domain.model.SortType interface AdventureRepository { - fun fetchMyAdventures(callback: (Result>) -> Unit) + suspend fun fetchMyAdventures(): List + suspend fun fetchAdventure(adventureId: Long): Adventure + suspend fun fetchAdventureByStatus(status: AdventureStatus): List + suspend fun beginAdventure(coordinate: Coordinate): Adventure - fun fetchAdventure(adventureId: Long, callback: (Result) -> Unit) - - fun fetchAdventureByStatus(status: AdventureStatus, callback: (Result>) -> Unit) - - fun beginAdventure(coordinate: Coordinate, callback: (Result) -> Unit) - - fun endGame( + suspend fun endGame( adventureId: Long, endType: AdventureEndType, coordinate: Coordinate, - callback: (Result) -> Unit, - ) + ): AdventureStatus - fun fetchAdventureResult(adventureId: Long, callback: (Result) -> Unit) + suspend fun fetchAdventureResult(adventureId: Long): AdventureResult - fun fetchMyAdventureResults( + suspend fun fetchMyAdventureResults( sortBy: SortType, order: OrderType, - callback: (Result>) -> Unit, - ) + ): List - fun fetchHint( + suspend fun fetchHint( adventureId: Long, hintId: Long, - callback: (Result) -> Unit, - ) + ): Hint - fun makeHint( + suspend fun makeHint( adventureId: Long, coordinate: Coordinate, - callback: (Result) -> Unit, - ) + ): Hint }