Skip to content

Commit 80ce18c

Browse files
committed
FEAT: 감정 구슬 선택 후 추천 루틴 표시 및 추천 루틴 등록 기능 구현
1 parent 38e8284 commit 80ce18c

11 files changed

Lines changed: 303 additions & 17 deletions

File tree

data/src/main/java/com/threegap/bitnagil/data/emotion/model/dto/EmotionRecommendedRoutineDto.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.threegap.bitnagil.data.emotion.model.dto
22

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

@@ -13,4 +14,12 @@ data class EmotionRecommendedRoutineDto(
1314
val recommendedRoutineDescription: String,
1415
@SerialName("recommendedSubRoutineSearchResult")
1516
val recommendedSubRoutineSearchResult: List<EmotionRecommendedSubRoutineDto>,
16-
)
17+
) {
18+
fun toEmotionRecommendRoutine() : EmotionRecommendRoutine {
19+
return EmotionRecommendRoutine(
20+
routineId = recommendedRoutineId.toString(),
21+
routineName = recommendedRoutineName,
22+
routineDescription = recommendedRoutineDescription,
23+
)
24+
}
25+
}

data/src/main/java/com/threegap/bitnagil/data/emotion/repositoryImpl/EmotionRepositoryImpl.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.threegap.bitnagil.data.emotion.repositoryImpl
33
import com.threegap.bitnagil.data.emotion.datasource.EmotionDataSource
44
import com.threegap.bitnagil.data.emotion.model.response.toDomain
55
import com.threegap.bitnagil.domain.emotion.model.Emotion
6+
import com.threegap.bitnagil.domain.emotion.model.EmotionRecommendRoutine
67
import com.threegap.bitnagil.domain.emotion.model.MyEmotion
78
import com.threegap.bitnagil.domain.emotion.repository.EmotionRepository
89
import javax.inject.Inject
@@ -26,7 +27,7 @@ class EmotionRepositoryImpl @Inject constructor(
2627
}
2728
}
2829

29-
override suspend fun registerEmotion(emotion: Emotion): Result<Unit> {
30+
override suspend fun registerEmotion(emotion: Emotion): Result<List<EmotionRecommendRoutine>> {
3031
val selectedEmotion = when (emotion) {
3132
Emotion.CALM -> "CALM"
3233
Emotion.VITALITY -> "VITALITY"
@@ -36,7 +37,11 @@ class EmotionRepositoryImpl @Inject constructor(
3637
Emotion.FATIGUE -> "FATIGUE"
3738
}
3839

39-
return emotionDataSource.registerEmotion(selectedEmotion).map { _ -> }
40+
return emotionDataSource.registerEmotion(selectedEmotion).map {
41+
it.recommendedRoutines.map {
42+
emotionRecommendedRoutineDto -> emotionRecommendedRoutineDto.toEmotionRecommendRoutine()
43+
}
44+
}
4045
}
4146

4247
override suspend fun getMyEmotionMarble(currentDate: String): Result<MyEmotion> =
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.threegap.bitnagil.domain.emotion.model
2+
3+
data class EmotionRecommendRoutine(
4+
val routineId: String,
5+
val routineName: String,
6+
val routineDescription: String,
7+
)
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package com.threegap.bitnagil.domain.emotion.repository
22

33
import com.threegap.bitnagil.domain.emotion.model.Emotion
4+
import com.threegap.bitnagil.domain.emotion.model.EmotionRecommendRoutine
45
import com.threegap.bitnagil.domain.emotion.model.MyEmotion
56

67
interface EmotionRepository {
78
suspend fun getEmotions(): Result<List<Emotion>>
8-
suspend fun registerEmotion(emotion: Emotion): Result<Unit>
9+
suspend fun registerEmotion(emotion: Emotion): Result<List<EmotionRecommendRoutine>>
910
suspend fun getMyEmotionMarble(currentDate: String): Result<MyEmotion>
1011
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package com.threegap.bitnagil.domain.emotion.usecase
22

33
import com.threegap.bitnagil.domain.emotion.model.Emotion
4+
import com.threegap.bitnagil.domain.emotion.model.EmotionRecommendRoutine
45
import com.threegap.bitnagil.domain.emotion.repository.EmotionRepository
56
import javax.inject.Inject
67

78
class RegisterEmotionUseCase @Inject constructor(
89
private val emotionRepository: EmotionRepository,
910
) {
10-
suspend operator fun invoke(emotion: Emotion): Result<Unit> {
11+
suspend operator fun invoke(emotion: Emotion): Result<List<EmotionRecommendRoutine>> {
1112
return emotionRepository.registerEmotion(emotion)
1213
}
1314
}

presentation/src/main/java/com/threegap/bitnagil/presentation/emotion/EmotionScreen.kt

Lines changed: 140 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.threegap.bitnagil.presentation.emotion
22

3+
import androidx.activity.compose.BackHandler
34
import androidx.compose.foundation.Image
45
import androidx.compose.foundation.background
56
import androidx.compose.foundation.layout.Arrangement
@@ -11,22 +12,32 @@ import androidx.compose.foundation.layout.padding
1112
import androidx.compose.foundation.lazy.grid.GridCells
1213
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
1314
import androidx.compose.foundation.lazy.grid.items
15+
import androidx.compose.foundation.rememberScrollState
16+
import androidx.compose.foundation.verticalScroll
1417
import androidx.compose.material3.Text
1518
import androidx.compose.runtime.Composable
1619
import androidx.compose.runtime.collectAsState
1720
import androidx.compose.runtime.getValue
1821
import androidx.compose.ui.Alignment
1922
import androidx.compose.ui.Modifier
23+
import androidx.compose.ui.graphics.Color
2024
import androidx.compose.ui.res.painterResource
2125
import androidx.compose.ui.text.style.TextAlign
26+
import androidx.compose.ui.text.style.TextDecoration
2227
import androidx.compose.ui.tooling.preview.Preview
2328
import androidx.compose.ui.unit.dp
2429
import androidx.hilt.navigation.compose.hiltViewModel
2530
import com.threegap.bitnagil.designsystem.BitnagilTheme
31+
import com.threegap.bitnagil.designsystem.component.atom.BitnagilSelectButton
32+
import com.threegap.bitnagil.designsystem.component.atom.BitnagilTextButton
33+
import com.threegap.bitnagil.designsystem.component.atom.BitnagilTextButtonColor
34+
import com.threegap.bitnagil.designsystem.component.block.BitnagilProgressTopBar
2635
import com.threegap.bitnagil.designsystem.component.block.BitnagilTopBar
2736
import com.threegap.bitnagil.designsystem.modifier.clickableWithoutRipple
2837
import com.threegap.bitnagil.presentation.common.flow.collectAsEffect
2938
import com.threegap.bitnagil.presentation.emotion.model.Emotion
39+
import com.threegap.bitnagil.presentation.emotion.model.EmotionRecommendRoutineUiModel
40+
import com.threegap.bitnagil.presentation.emotion.model.EmotionScreenStep
3041
import com.threegap.bitnagil.presentation.emotion.model.mvi.EmotionSideEffect
3142
import com.threegap.bitnagil.presentation.emotion.model.mvi.EmotionState
3243

@@ -37,17 +48,30 @@ fun EmotionScreenContainer(
3748
) {
3849
val state by viewModel.stateFlow.collectAsState()
3950

51+
BackHandler {
52+
viewModel.moveToPrev()
53+
}
54+
4055
viewModel.sideEffectFlow.collectAsEffect { sideEffect ->
4156
when (sideEffect) {
4257
EmotionSideEffect.NavigateToBack -> navigateToBack()
4358
}
4459
}
4560

46-
EmotionScreen(
47-
state = state,
48-
onClickPreviousButton = navigateToBack,
49-
onClickEmotion = viewModel::selectEmotion,
50-
)
61+
when(state.step) {
62+
EmotionScreenStep.Emotion -> EmotionScreen(
63+
state = state,
64+
onClickPreviousButton = navigateToBack,
65+
onClickEmotion = viewModel::selectEmotion,
66+
)
67+
EmotionScreenStep.RecommendRoutines -> EmotionRecommendRoutineScreen(
68+
state = state,
69+
onClickPreviousButton = viewModel::moveToPrev,
70+
onClickRoutine = viewModel::selectRecommendRoutine,
71+
onClickRegisterRecommendRoutines = viewModel::registerRecommendRoutines,
72+
onClickSkip = navigateToBack
73+
)
74+
}
5175
}
5276

5377
@Composable
@@ -111,17 +135,127 @@ private fun EmotionScreen(
111135
}
112136
}
113137

138+
@Composable
139+
private fun EmotionRecommendRoutineScreen(
140+
state: EmotionState,
141+
onClickPreviousButton: () -> Unit,
142+
onClickRoutine: (String) -> Unit,
143+
onClickRegisterRecommendRoutines: () -> Unit,
144+
onClickSkip: () -> Unit,
145+
) {
146+
Column(
147+
modifier = Modifier
148+
.fillMaxSize()
149+
.background(color = BitnagilTheme.colors.coolGray99),
150+
) {
151+
BitnagilProgressTopBar(
152+
onBackClick = onClickPreviousButton,
153+
progress = 1f,
154+
)
155+
156+
Column(
157+
modifier = Modifier
158+
.weight(1f)
159+
.padding(start = 16.dp, end = 16.dp, bottom = 20.dp, top = 32.dp),
160+
) {
161+
Text(
162+
text = "오늘 감정에 따른\n루틴을 추천드릴께요!",
163+
color = BitnagilTheme.colors.navy500,
164+
style = BitnagilTheme.typography.title2Bold,
165+
)
166+
167+
168+
Spacer(modifier = Modifier.height(10.dp))
169+
170+
Text(
171+
text = "오늘 당신의 감정 상태에 맞춰 구성된 맞춤 루틴이에요.\n원하는 루틴을 선택해서 가볍게 시작해보세요.",
172+
color = BitnagilTheme.colors.coolGray50,
173+
style = BitnagilTheme.typography.body2Medium,
174+
)
175+
176+
177+
Spacer(modifier = Modifier.height(28.dp))
178+
179+
val scrollState = rememberScrollState()
180+
Column(
181+
modifier = Modifier
182+
.weight(1f)
183+
.verticalScroll(state = scrollState),
184+
) {
185+
for (recommendRoutine in state.recommendRoutines) {
186+
BitnagilSelectButton(
187+
title = recommendRoutine.name,
188+
description = recommendRoutine.description,
189+
onClick = { onClickRoutine(recommendRoutine.id) },
190+
selected = recommendRoutine.selected,
191+
modifier = Modifier.padding(bottom = 12.dp),
192+
)
193+
}
194+
}
195+
196+
Spacer(modifier = Modifier.height(12.dp))
197+
198+
BitnagilTextButton(
199+
text = "변경하기",
200+
onClick = onClickRegisterRecommendRoutines,
201+
enabled = state.registerRecommendRoutinesButtonEnabled,
202+
)
203+
204+
Spacer(modifier = Modifier.height(10.dp))
205+
206+
BitnagilTextButton(
207+
text = "건너뛰기",
208+
onClick = onClickSkip,
209+
colors = BitnagilTextButtonColor.skip().copy(
210+
defaultBackgroundColor = Color.Transparent,
211+
pressedBackgroundColor = Color.Transparent,
212+
disabledBackgroundColor = Color.Transparent,
213+
),
214+
textStyle = BitnagilTheme.typography.body2Regular,
215+
textDecoration = TextDecoration.Underline,
216+
)
217+
}
218+
}
219+
}
220+
114221
@Preview
115222
@Composable
116-
fun MyPageScreenPreview() {
223+
private fun EmotionScreenPreview() {
117224
BitnagilTheme {
118225
EmotionScreen(
119226
state = EmotionState(
120227
emotions = Emotion.entries,
121228
isLoading = false,
229+
step = EmotionScreenStep.Emotion,
122230
),
123231
onClickEmotion = { _ -> },
124232
onClickPreviousButton = {},
125233
)
126234
}
127235
}
236+
237+
@Preview
238+
@Composable
239+
private fun EmotionRecommendRoutineScreenPreview() {
240+
BitnagilTheme {
241+
EmotionRecommendRoutineScreen(
242+
state = EmotionState(
243+
emotions = Emotion.entries,
244+
isLoading = false,
245+
step = EmotionScreenStep.RecommendRoutines,
246+
recommendRoutines = listOf(
247+
EmotionRecommendRoutineUiModel(
248+
id = "1",
249+
name = "루틴 이름",
250+
description = "루틴 설명",
251+
selected = true,
252+
)
253+
)
254+
),
255+
onClickPreviousButton = {},
256+
onClickRoutine = {},
257+
onClickRegisterRecommendRoutines = {},
258+
onClickSkip = {},
259+
)
260+
}
261+
}

0 commit comments

Comments
 (0)