Skip to content

Commit e61f183

Browse files
committed
Refactor: 루틴 완료 api 버전 업
- v1 -> v2로 마이그레이션 - 루틴 완료 로직 인덱스 값 기반으로 수정 - 루틴 완료 requestDto 수정
1 parent 5b57cc8 commit e61f183

15 files changed

Lines changed: 167 additions & 235 deletions

File tree

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
package com.threegap.bitnagil.data.routine.model.request
22

3+
import com.threegap.bitnagil.domain.routine.model.RoutineCompletionInfo
34
import kotlinx.serialization.SerialName
45
import kotlinx.serialization.Serializable
56

67
@Serializable
78
data class RoutineCompletionInfoDto(
8-
@SerialName("routineType")
9-
val routineType: String,
109
@SerialName("routineId")
1110
val routineId: String,
12-
@SerialName("historySeq")
13-
val historySeq: Int,
14-
@SerialName("completeYn")
15-
val isCompleted: Boolean,
11+
@SerialName("routineCompleteYn")
12+
val routineCompleteYn: Boolean,
13+
@SerialName("subRoutineCompleteYn")
14+
val subRoutineCompleteYn: List<Boolean>,
1615
)
16+
17+
internal fun RoutineCompletionInfo.toDto() =
18+
RoutineCompletionInfoDto(
19+
routineId = this.routineId,
20+
routineCompleteYn = this.routineCompleteYn,
21+
subRoutineCompleteYn = this.subRoutineCompleteYn,
22+
)
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package com.threegap.bitnagil.data.routine.model.request
22

3+
import com.threegap.bitnagil.domain.routine.model.RoutineCompletionInfos
34
import kotlinx.serialization.SerialName
45
import kotlinx.serialization.Serializable
56

67
@Serializable
78
data class RoutineCompletionRequestDto(
8-
@SerialName("performedDate")
9-
val performedDate: String,
109
@SerialName("routineCompletionInfos")
11-
val routineCompletions: List<RoutineCompletionInfoDto>,
10+
val routineCompletionInfos: List<RoutineCompletionInfoDto>,
1211
)
12+
13+
internal fun RoutineCompletionInfos.toDto() =
14+
RoutineCompletionRequestDto(
15+
routineCompletionInfos = this.routineCompletionInfos.map { it.toDto() },
16+
)

data/src/main/java/com/threegap/bitnagil/data/routine/repositoryImpl/RoutineRepositoryImpl.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package com.threegap.bitnagil.data.routine.repositoryImpl
22

33
import com.threegap.bitnagil.data.routine.datasource.RoutineRemoteDataSource
4-
import com.threegap.bitnagil.data.routine.mapper.toDomain
5-
import com.threegap.bitnagil.data.routine.mapper.toDto
64
import com.threegap.bitnagil.data.routine.model.request.toDto
5+
import com.threegap.bitnagil.data.routine.model.response.toDomain
76
import com.threegap.bitnagil.domain.routine.model.Routine
87
import com.threegap.bitnagil.domain.routine.model.RoutineByDayDeletion
9-
import com.threegap.bitnagil.domain.routine.model.RoutineCompletion
8+
import com.threegap.bitnagil.domain.routine.model.RoutineCompletionInfos
109
import com.threegap.bitnagil.domain.routine.model.Routines
1110
import com.threegap.bitnagil.domain.routine.repository.RoutineRepository
1211
import javax.inject.Inject
@@ -18,8 +17,8 @@ class RoutineRepositoryImpl @Inject constructor(
1817
routineRemoteDataSource.fetchWeeklyRoutines(startDate, endDate)
1918
.map { it.toDomain() }
2019

21-
override suspend fun syncRoutineCompletion(routineCompletion: RoutineCompletion): Result<Unit> =
22-
routineRemoteDataSource.syncRoutineCompletion(routineCompletion.toDto())
20+
override suspend fun syncRoutineCompletion(routineCompletionInfos: RoutineCompletionInfos): Result<Unit> =
21+
routineRemoteDataSource.syncRoutineCompletion(routineCompletionInfos.toDto())
2322

2423
override suspend fun deleteRoutine(routineId: String): Result<Unit> =
2524
routineRemoteDataSource.deleteRoutine(routineId)

data/src/main/java/com/threegap/bitnagil/data/routine/service/RoutineService.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import retrofit2.http.Body
99
import retrofit2.http.DELETE
1010
import retrofit2.http.GET
1111
import retrofit2.http.HTTP
12-
import retrofit2.http.POST
12+
import retrofit2.http.PUT
1313
import retrofit2.http.Path
1414
import retrofit2.http.Query
1515

@@ -20,7 +20,7 @@ interface RoutineService {
2020
@Query("endDate") endDate: String,
2121
): BaseResponse<RoutinesResponseDto>
2222

23-
@POST("/api/v1/routines/completions")
23+
@PUT("/api/v2/routines")
2424
suspend fun routineCompletion(
2525
@Body request: RoutineCompletionRequestDto,
2626
): BaseResponse<Unit>
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
package com.threegap.bitnagil.domain.routine.model
22

33
data class RoutineCompletionInfo(
4-
val routineType: RoutineType,
54
val routineId: String,
6-
val historySeq: Int,
7-
val isCompleted: Boolean,
5+
val routineCompleteYn: Boolean,
6+
val subRoutineCompleteYn: List<Boolean>,
87
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.threegap.bitnagil.domain.routine.model
2+
3+
data class RoutineCompletionInfos(
4+
val routineCompletionInfos: List<RoutineCompletionInfo>,
5+
)

domain/src/main/java/com/threegap/bitnagil/domain/routine/repository/RoutineRepository.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ package com.threegap.bitnagil.domain.routine.repository
22

33
import com.threegap.bitnagil.domain.routine.model.Routine
44
import com.threegap.bitnagil.domain.routine.model.RoutineByDayDeletion
5-
import com.threegap.bitnagil.domain.routine.model.RoutineCompletion
5+
import com.threegap.bitnagil.domain.routine.model.RoutineCompletionInfos
66
import com.threegap.bitnagil.domain.routine.model.Routines
77

88
interface RoutineRepository {
99
suspend fun fetchWeeklyRoutines(startDate: String, endDate: String): Result<Routines>
10-
suspend fun syncRoutineCompletion(routineCompletion: RoutineCompletion): Result<Unit>
10+
suspend fun syncRoutineCompletion(routineCompletionInfos: RoutineCompletionInfos): Result<Unit>
1111
suspend fun deleteRoutine(routineId: String): Result<Unit>
1212
suspend fun getRoutine(routineId: String): Result<Routine>
1313
suspend fun deleteRoutineByDay(routineByDayDeletion: RoutineByDayDeletion): Result<Unit>
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package com.threegap.bitnagil.domain.routine.usecase
22

3-
import com.threegap.bitnagil.domain.routine.model.RoutineCompletion
3+
import com.threegap.bitnagil.domain.routine.model.RoutineCompletionInfos
44
import com.threegap.bitnagil.domain.routine.repository.RoutineRepository
55
import javax.inject.Inject
66

77
class RoutineCompletionUseCase @Inject constructor(
88
private val routineRepository: RoutineRepository,
99
) {
10-
suspend operator fun invoke(routineCompletion: RoutineCompletion): Result<Unit> =
11-
routineRepository.syncRoutineCompletion(routineCompletion)
10+
suspend operator fun invoke(routineCompletionInfos: RoutineCompletionInfos): Result<Unit> =
11+
routineRepository.syncRoutineCompletion(routineCompletionInfos)
1212
}

presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeScreen.kt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ fun HomeScreenContainer(
7878
onRoutineCompletionToggle = { routineId, isCompleted ->
7979
viewModel.toggleRoutineCompletion(routineId, isCompleted)
8080
},
81-
onSubRoutineCompletionToggle = { routineId, subRoutineId, isCompleted ->
82-
viewModel.toggleSubRoutineCompletion(routineId, subRoutineId, isCompleted)
81+
onSubRoutineCompletionToggle = { routineId, subRoutineIndex, isCompleted ->
82+
viewModel.toggleSubRoutineCompletion(routineId, subRoutineIndex, isCompleted)
8383
},
8484
onRegisterRoutineClick = {
8585
viewModel.sendIntent(HomeIntent.OnRegisterRoutineClick)
@@ -100,7 +100,7 @@ private fun HomeScreen(
100100
onPreviousWeekClick: () -> Unit,
101101
onNextWeekClick: () -> Unit,
102102
onRoutineCompletionToggle: (String, Boolean) -> Unit,
103-
onSubRoutineCompletionToggle: (String, String, Boolean) -> Unit,
103+
onSubRoutineCompletionToggle: (String, Int, Boolean) -> Unit,
104104
onRegisterRoutineClick: () -> Unit,
105105
onRegisterEmotionClick: () -> Unit,
106106
onShowMoreRoutinesClick: () -> Unit,
@@ -170,8 +170,7 @@ private fun HomeScreen(
170170
.fillMaxSize()
171171
.background(BitnagilTheme.colors.coolGray99)
172172
.nestedScroll(collapsibleHeaderState.nestedScrollConnection)
173-
.padding(horizontal = 16.dp)
174-
.padding(bottom = 12.dp),
173+
.padding(horizontal = 16.dp),
175174
state = collapsibleHeaderState.lazyListState,
176175
verticalArrangement = Arrangement.spacedBy(12.dp),
177176
) {
@@ -187,10 +186,10 @@ private fun HomeScreen(
187186
isCompleted,
188187
)
189188
},
190-
onSubRoutineToggle = { subRoutineId, isCompleted ->
189+
onSubRoutineToggle = { subRoutineIndex, isCompleted ->
191190
onSubRoutineCompletionToggle(
192191
routine.routineId,
193-
subRoutineId,
192+
subRoutineIndex,
194193
isCompleted,
195194
)
196195
},

presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeViewModel.kt

Lines changed: 40 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import androidx.lifecycle.viewModelScope
66
import com.threegap.bitnagil.domain.emotion.usecase.FetchTodayEmotionUseCase
77
import com.threegap.bitnagil.domain.emotion.usecase.GetEmotionChangeEventFlowUseCase
88
import com.threegap.bitnagil.domain.onboarding.usecase.GetOnBoardingRecommendRoutineEventFlowUseCase
9-
import com.threegap.bitnagil.domain.routine.model.RoutineCompletion
109
import com.threegap.bitnagil.domain.routine.model.RoutineCompletionInfo
10+
import com.threegap.bitnagil.domain.routine.model.RoutineCompletionInfos
1111
import com.threegap.bitnagil.domain.routine.usecase.FetchWeeklyRoutinesUseCase
1212
import com.threegap.bitnagil.domain.routine.usecase.RoutineCompletionUseCase
1313
import com.threegap.bitnagil.domain.user.usecase.FetchUserProfileUseCase
@@ -103,7 +103,7 @@ class HomeViewModel @Inject constructor(
103103
}
104104

105105
is HomeIntent.OnSubRoutineCompletionToggle -> {
106-
updateSubRoutine(state, intent.routineId, intent.subRoutineId, intent.isCompleted)
106+
updateSubRoutine(state, intent.routineId, intent.subRoutineIndex, intent.isCompleted)
107107
}
108108

109109
is HomeIntent.LoadTodayEmotion -> {
@@ -201,8 +201,7 @@ class HomeViewModel @Inject constructor(
201201
viewModelScope.launch {
202202
fetchWeeklyRoutinesUseCase(startDate, endDate).fold(
203203
onSuccess = { routines ->
204-
val routinesUiModel = routines.toUiModel()
205-
sendIntent(HomeIntent.LoadWeeklyRoutines(routinesUiModel))
204+
sendIntent(HomeIntent.LoadWeeklyRoutines(routines.toUiModel()))
206205
sendIntent(HomeIntent.UpdateLoading(false))
207206
},
208207
onFailure = { error ->
@@ -237,11 +236,11 @@ class HomeViewModel @Inject constructor(
237236
processRoutineToggleChanges(originalState, predictedUpdatedState)
238237
}
239238

240-
fun toggleSubRoutineCompletion(routineId: String, subRoutineId: String, isCompleted: Boolean) {
239+
fun toggleSubRoutineCompletion(routineId: String, subRoutineIndex: Int, isCompleted: Boolean) {
241240
val originalState = container.stateFlow.value
242-
sendIntent(HomeIntent.OnSubRoutineCompletionToggle(routineId, subRoutineId, isCompleted))
241+
sendIntent(HomeIntent.OnSubRoutineCompletionToggle(routineId, subRoutineIndex, isCompleted))
243242

244-
val predictedUpdatedState = updateSubRoutine(originalState, routineId, subRoutineId, isCompleted)
243+
val predictedUpdatedState = updateSubRoutine(originalState, routineId, subRoutineIndex, isCompleted)
245244
processRoutineToggleChanges(originalState, predictedUpdatedState)
246245
}
247246

@@ -278,11 +277,10 @@ class HomeViewModel @Inject constructor(
278277
val routineIndex = routinesForDate.indexOfFirst { it.routineId == routineId }
279278
if (routineIndex == -1) return@updateRoutinesForDate false
280279

281-
val updatedRoutine = routinesForDate[routineIndex].copy(
282-
isCompleted = isCompleted,
283-
subRoutines = routinesForDate[routineIndex].subRoutines.map { subRoutine ->
284-
subRoutine.copy(isCompleted = isCompleted)
285-
},
280+
val routine = routinesForDate[routineIndex]
281+
val updatedRoutine = routine.copy(
282+
routineCompleteYn = isCompleted,
283+
subRoutineCompleteYn = routine.subRoutineCompleteYn.map { isCompleted },
286284
)
287285

288286
routinesForDate[routineIndex] = updatedRoutine
@@ -293,27 +291,34 @@ class HomeViewModel @Inject constructor(
293291
private fun updateSubRoutine(
294292
state: HomeState,
295293
routineId: String,
296-
subRoutineId: String,
294+
subRoutineIndex: Int,
297295
isCompleted: Boolean,
298296
): HomeState {
299297
return updateRoutinesForDate(state) { routinesForDate ->
300298
val routineIndex = routinesForDate.indexOfFirst { it.routineId == routineId }
301299
if (routineIndex == -1) return@updateRoutinesForDate false
302300

303301
val routine = routinesForDate[routineIndex]
304-
val updatedSubRoutines = routine.subRoutines.map { subRoutine ->
305-
if (subRoutine.subRoutineId == subRoutineId) {
306-
subRoutine.copy(isCompleted = isCompleted)
307-
} else {
308-
subRoutine
302+
303+
if (subRoutineIndex < 0 || subRoutineIndex >= routine.subRoutineNames.size) {
304+
return@updateRoutinesForDate false
305+
}
306+
307+
val updatedSubRoutineCompleteYn = routine.subRoutineCompleteYn.toMutableList().apply {
308+
if (subRoutineIndex < size) {
309+
this[subRoutineIndex] = isCompleted
309310
}
310311
}
311312

312-
val routineCompleted = if (isCompleted) updatedSubRoutines.all { it.isCompleted } else false
313+
val routineCompleted = if (isCompleted) {
314+
updatedSubRoutineCompleteYn.all { it }
315+
} else {
316+
false
317+
}
313318

314319
val updatedRoutine = routine.copy(
315-
subRoutines = updatedSubRoutines,
316-
isCompleted = routineCompleted,
320+
subRoutineCompleteYn = updatedSubRoutineCompleteYn,
321+
routineCompleteYn = routineCompleted,
317322
)
318323

319324
routinesForDate[routineIndex] = updatedRoutine
@@ -326,12 +331,13 @@ class HomeViewModel @Inject constructor(
326331
updateLogic: (MutableList<RoutineUiModel>) -> Boolean,
327332
): HomeState {
328333
val dateKey = state.selectedDate.toString()
329-
val routinesForDate = state.routines.routinesByDate[dateKey]?.toMutableList() ?: return state
334+
val routinesForDate = state.routines.routines[dateKey]?.routineList?.toMutableList() ?: return state
330335

331336
if (!updateLogic(routinesForDate)) return state
332337

333-
val updatedRoutinesByDate = state.routines.routinesByDate.toMutableMap()
334-
updatedRoutinesByDate[dateKey] = routinesForDate
338+
val updatedRoutinesByDate = state.routines.routines.toMutableMap()
339+
val dayRoutines = updatedRoutinesByDate[dateKey] ?: return state
340+
updatedRoutinesByDate[dateKey] = dayRoutines.copy(routineList = routinesForDate)
335341

336342
return state.copy(routines = RoutinesUiModel(updatedRoutinesByDate))
337343
}
@@ -342,39 +348,25 @@ class HomeViewModel @Inject constructor(
342348
date: LocalDate,
343349
): List<RoutineCompletionInfo> {
344350
val dateKey = date.toString()
345-
val originalRoutineList = originalRoutines.routinesByDate[dateKey] ?: emptyList()
346-
val updatedRoutineList = updatedRoutines.routinesByDate[dateKey] ?: emptyList()
351+
val originalRoutineList = originalRoutines.routines[dateKey]?.routineList ?: emptyList()
352+
val updatedRoutineList = updatedRoutines.routines[dateKey]?.routineList ?: emptyList()
347353

348354
return buildList {
349355
updatedRoutineList.forEach { updatedRoutine ->
350356
val originalRoutine = originalRoutineList.find { it.routineId == updatedRoutine.routineId }
351357

352-
if (originalRoutine?.isCompleted != updatedRoutine.isCompleted) {
358+
val hasMainRoutineChanged = originalRoutine?.routineCompleteYn != updatedRoutine.routineCompleteYn
359+
val hasSubRoutinesChanged = originalRoutine?.subRoutineCompleteYn != updatedRoutine.subRoutineCompleteYn
360+
361+
if (hasMainRoutineChanged || hasSubRoutinesChanged) {
353362
add(
354363
RoutineCompletionInfo(
355-
routineType = updatedRoutine.routineType,
356364
routineId = updatedRoutine.routineId,
357-
historySeq = updatedRoutine.historySeq,
358-
isCompleted = updatedRoutine.isCompleted,
365+
routineCompleteYn = updatedRoutine.routineCompleteYn,
366+
subRoutineCompleteYn = updatedRoutine.subRoutineCompleteYn,
359367
),
360368
)
361369
}
362-
363-
updatedRoutine.subRoutines.forEach { updatedSubRoutine ->
364-
val originalSubRoutine = originalRoutine?.subRoutines
365-
?.find { it.subRoutineId == updatedSubRoutine.subRoutineId }
366-
367-
if (originalSubRoutine?.isCompleted != updatedSubRoutine.isCompleted) {
368-
add(
369-
RoutineCompletionInfo(
370-
routineType = updatedSubRoutine.routineType,
371-
routineId = updatedSubRoutine.subRoutineId,
372-
historySeq = updatedSubRoutine.historySeq,
373-
isCompleted = updatedSubRoutine.isCompleted,
374-
),
375-
)
376-
}
377-
}
378370
}
379371
}
380372
}
@@ -385,9 +377,8 @@ class HomeViewModel @Inject constructor(
385377

386378
if (unsyncedChanges.isEmpty()) return
387379

388-
val syncRequest = RoutineCompletion(
389-
performedDate = dateKey,
390-
routineCompletions = unsyncedChanges.toList(),
380+
val syncRequest = RoutineCompletionInfos(
381+
routineCompletionInfos = unsyncedChanges.toList(),
391382
)
392383

393384
routineCompletionUseCase(syncRequest).fold(

0 commit comments

Comments
 (0)