Skip to content

Commit d943aaa

Browse files
committed
REFACTOR: 온보딩 화면 리디자인 UI 적용
1 parent dc98357 commit d943aaa

9 files changed

Lines changed: 377 additions & 80 deletions

File tree

core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilProgressBar.kt

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import androidx.compose.runtime.remember
2020
import androidx.compose.runtime.setValue
2121
import androidx.compose.ui.Modifier
2222
import androidx.compose.ui.geometry.Offset
23-
import androidx.compose.ui.graphics.Brush
2423
import androidx.compose.ui.graphics.Color
2524
import androidx.compose.ui.graphics.StrokeCap
2625
import androidx.compose.ui.tooling.preview.Preview
@@ -59,11 +58,7 @@ fun BitnagilProgressBar(
5958
if (animatedProgress > 0) {
6059
val progressEndX = size.width * animatedProgress
6160
drawLine(
62-
brush = Brush.horizontalGradient(
63-
colors = listOf(color.gradientStartColor, color.gradientEndColor),
64-
startX = 0f,
65-
endX = progressEndX,
66-
),
61+
color = color.progressColor,
6762
start = Offset(0f, size.height / 2),
6863
end = Offset(progressEndX, size.height / 2),
6964
strokeWidth = strokeWidth,
@@ -75,16 +70,14 @@ fun BitnagilProgressBar(
7570

7671
@Immutable
7772
data class BitnagilProgressBarColor(
78-
val gradientStartColor: Color,
79-
val gradientEndColor: Color,
73+
val progressColor: Color,
8074
val backgroundColor: Color,
8175
) {
8276
companion object {
8377
@Composable
8478
fun default(): BitnagilProgressBarColor = BitnagilProgressBarColor(
85-
gradientStartColor = BitnagilTheme.colors.progressBarGradientStartColor,
86-
gradientEndColor = BitnagilTheme.colors.progressBarGradientEndColor,
87-
backgroundColor = BitnagilTheme.colors.white,
79+
progressColor = BitnagilTheme.colors.orange500,
80+
backgroundColor = BitnagilTheme.colors.coolGray97,
8881
)
8982
}
9083
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.threegap.bitnagil.presentation.common.ninepatch
2+
3+
import android.graphics.drawable.NinePatchDrawable
4+
import androidx.compose.ui.Modifier
5+
import androidx.compose.ui.draw.drawBehind
6+
import androidx.compose.ui.graphics.nativeCanvas
7+
import androidx.compose.ui.platform.LocalContext
8+
import androidx.compose.runtime.Composable
9+
import androidx.compose.runtime.remember
10+
import androidx.core.content.ContextCompat
11+
import androidx.core.graphics.drawable.updateBounds
12+
13+
@Composable
14+
fun Modifier.ninePatchBackgroundNode(drawableResId: Int): Modifier {
15+
val context = LocalContext.current
16+
val ninePatchDrawable = remember(drawableResId) {
17+
ContextCompat.getDrawable(context, drawableResId) as? NinePatchDrawable
18+
}
19+
20+
return if (ninePatchDrawable != null) {
21+
this.then(
22+
Modifier.drawBehind {
23+
ninePatchDrawable.updateBounds(0, 0, size.width.toInt(), size.height.toInt())
24+
ninePatchDrawable.draw(drawContext.canvas.nativeCanvas)
25+
}
26+
)
27+
} else {
28+
this
29+
}
30+
}

presentation/src/main/java/com/threegap/bitnagil/presentation/onboarding/OnBoardingScreen.kt

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.threegap.bitnagil.designsystem.BitnagilTheme
1414
import com.threegap.bitnagil.designsystem.component.block.BitnagilProgressTopBar
1515
import com.threegap.bitnagil.presentation.common.flow.collectAsEffect
1616
import com.threegap.bitnagil.presentation.onboarding.component.template.OnBoardingAbstractTemplate
17+
import com.threegap.bitnagil.presentation.onboarding.component.template.OnBoardingIntroTemplate
1718
import com.threegap.bitnagil.presentation.onboarding.component.template.OnBoardingSelectTemplate
1819
import com.threegap.bitnagil.presentation.onboarding.model.OnBoardingItem
1920
import com.threegap.bitnagil.presentation.onboarding.model.OnBoardingPageInfo
@@ -67,24 +68,34 @@ private fun OnBoardingScreen(
6768
) {
6869
Column(
6970
modifier = Modifier
70-
.background(BitnagilTheme.colors.coolGray99)
71+
.background(BitnagilTheme.colors.white)
7172
.statusBarsPadding(),
7273
) {
73-
BitnagilProgressTopBar(
74-
onBackClick = onClickPreviousInSelectOnBoarding,
75-
progress = state.progress,
76-
)
74+
if (state.showProgress)
75+
BitnagilProgressTopBar(
76+
modifier = Modifier,
77+
onBackClick = onClickPreviousInSelectOnBoarding,
78+
progress = state.progress,
79+
)
7780

7881
when (state) {
7982
is OnBoardingState.Idle -> {
8083
when (val currentOnBoardingPageInfo = state.currentOnBoardingPageInfo) {
84+
OnBoardingPageInfo.Intro -> {
85+
OnBoardingIntroTemplate(
86+
userName = state.userName,
87+
onClickNextButton = onClickNext
88+
)
89+
}
8190
is OnBoardingPageInfo.Abstract -> {
8291
OnBoardingAbstractTemplate(
8392
modifier = Modifier.weight(1f),
84-
title = "이제 당신에게\n꼭 맞는 루틴을 제안해드릴게요.",
85-
onInit = loadRecommendRoutines,
93+
title = "이제 포모가 당신에게\n꼭 맞는 루틴을 찾아줄거에요.",
8694
onBoardingAbstractTexts = currentOnBoardingPageInfo.abstractTexts,
8795
onDispose = cancelRecommendRoutines,
96+
onClickNextButton = loadRecommendRoutines,
97+
nextButtonEnable = state.nextButtonEnable,
98+
userName = state.userName,
8899
)
89100
}
90101
is OnBoardingPageInfo.RecommendRoutines -> {
@@ -141,6 +152,7 @@ fun OnBoardingScreenPreview() {
141152
totalStep = 5,
142153
currentStep = 1,
143154
onBoardingSetType = OnBoardingSetType.RESET,
155+
userName = "안드로이드"
144156
),
145157
onClickNext = {},
146158
onClickPreviousInSelectOnBoarding = {},

presentation/src/main/java/com/threegap/bitnagil/presentation/onboarding/OnBoardingViewModel.kt

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.threegap.bitnagil.domain.onboarding.usecase.GetOnBoardingAbstractUseC
66
import com.threegap.bitnagil.domain.onboarding.usecase.GetOnBoardingsUseCase
77
import com.threegap.bitnagil.domain.onboarding.usecase.GetRecommendOnBoardingRoutinesUseCase
88
import com.threegap.bitnagil.domain.onboarding.usecase.RegisterRecommendOnBoardingRoutinesUseCase
9+
import com.threegap.bitnagil.domain.user.usecase.FetchUserProfileUseCase
910
import com.threegap.bitnagil.presentation.common.mviviewmodel.MviViewModel
1011
import com.threegap.bitnagil.presentation.onboarding.model.OnBoardingAbstractTextItem
1112
import com.threegap.bitnagil.presentation.onboarding.model.OnBoardingItem
@@ -21,7 +22,6 @@ import dagger.assisted.AssistedInject
2122
import dagger.hilt.android.lifecycle.HiltViewModel
2223
import kotlinx.coroutines.Job
2324
import kotlinx.coroutines.async
24-
import kotlinx.coroutines.delay
2525
import kotlinx.coroutines.isActive
2626
import kotlinx.coroutines.launch
2727
import org.orbitmvi.orbit.syntax.simple.SimpleSyntax
@@ -33,6 +33,7 @@ class OnBoardingViewModel @AssistedInject constructor(
3333
private val getRecommendOnBoardingRoutinesUseCase: GetRecommendOnBoardingRoutinesUseCase,
3434
private val getOnBoardingAbstractUseCase: GetOnBoardingAbstractUseCase,
3535
private val registerRecommendOnBoardingRoutinesUseCase: RegisterRecommendOnBoardingRoutinesUseCase,
36+
private val fetchUserProfileUseCase: FetchUserProfileUseCase,
3637
@Assisted private val onBoardingArg: OnBoardingScreenArg,
3738
) : MviViewModel<OnBoardingState, OnBoardingSideEffect, OnBoardingIntent>(
3839
initState = OnBoardingState.Loading,
@@ -43,7 +44,7 @@ class OnBoardingViewModel @AssistedInject constructor(
4344
}
4445

4546
// 내부에 전체 온보딩 항목 저장
46-
private val onBoardingPageInfos = mutableListOf<OnBoardingPageInfo.SelectOnBoarding>()
47+
private val selectOnBoardingPageInfos = mutableListOf<OnBoardingPageInfo.SelectOnBoarding>()
4748

4849
private var loadRecommendRoutinesJob: Job? = null
4950

@@ -54,12 +55,21 @@ class OnBoardingViewModel @AssistedInject constructor(
5455
private fun loadOnBoardingItems() {
5556
viewModelScope.launch {
5657
val onBoardings = getOnBoardingsUseCase()
57-
5858
val onBoardingPages = onBoardings.map { onBoarding ->
5959
OnBoardingPageInfo.SelectOnBoarding.fromOnBoarding(onBoarding = onBoarding)
6060
}
6161

62-
sendIntent(intent = OnBoardingIntent.LoadOnBoardingSuccess(onBoardingPageInfos = onBoardingPages))
62+
val userProfile = fetchUserProfileUseCase()
63+
val userName = userProfile.fold(
64+
onSuccess = {
65+
return@fold it.nickname
66+
},
67+
onFailure = {
68+
return@fold "-"
69+
},
70+
)
71+
72+
sendIntent(intent = OnBoardingIntent.LoadOnBoardingSuccess(onBoardingPageInfos = onBoardingPages, userName = userName))
6373
}
6474
}
6575

@@ -69,15 +79,22 @@ class OnBoardingViewModel @AssistedInject constructor(
6979
): OnBoardingState? {
7080
when (intent) {
7181
is OnBoardingIntent.LoadOnBoardingSuccess -> {
72-
onBoardingPageInfos.clear()
73-
onBoardingPageInfos.addAll(intent.onBoardingPageInfos)
82+
selectOnBoardingPageInfos.clear()
83+
selectOnBoardingPageInfos.addAll(intent.onBoardingPageInfos)
84+
85+
val onBoardingSetType = OnBoardingSetType.fromOnBoardingScreenArg(onBoardingArg)
86+
val firstPage = when(onBoardingSetType) {
87+
OnBoardingSetType.NEW -> OnBoardingPageInfo.Intro
88+
OnBoardingSetType.RESET -> OnBoardingPageInfo.Intro
89+
}
7490

7591
return OnBoardingState.Idle(
76-
nextButtonEnable = false,
77-
currentOnBoardingPageInfo = onBoardingPageInfos.first(),
78-
totalStep = onBoardingPageInfos.size + 2,
79-
currentStep = 1,
80-
onBoardingSetType = OnBoardingSetType.fromOnBoardingScreenArg(onBoardingArg),
92+
nextButtonEnable = true,
93+
currentOnBoardingPageInfo = firstPage,
94+
totalStep = selectOnBoardingPageInfos.size + 2,
95+
currentStep = 0,
96+
onBoardingSetType = onBoardingSetType,
97+
userName = intent.userName
8198
)
8299
}
83100

@@ -89,7 +106,7 @@ class OnBoardingViewModel @AssistedInject constructor(
89106
if (currentPageInfo !is OnBoardingPageInfo.SelectOnBoarding) return null
90107

91108
val selectChangedCurrentPageInfo = currentPageInfo.selectItem(itemId = intent.itemId)
92-
onBoardingPageInfos[currentState.currentStep - 1] = selectChangedCurrentPageInfo
109+
selectOnBoardingPageInfos[currentState.currentStep - 1] = selectChangedCurrentPageInfo
93110
return currentState.copy(
94111
currentOnBoardingPageInfo = selectChangedCurrentPageInfo,
95112
nextButtonEnable = selectChangedCurrentPageInfo.isItemSelected,
@@ -100,10 +117,10 @@ class OnBoardingViewModel @AssistedInject constructor(
100117
val currentState = state
101118
if (currentState !is OnBoardingState.Idle) return null
102119

103-
val isLastSelectOnBoarding = currentState.currentStep >= onBoardingPageInfos.size
104-
if (isLastSelectOnBoarding) return null
120+
val isLastPageOfSelectOnBoarding = currentState.currentStep >= selectOnBoardingPageInfos.size
121+
if (isLastPageOfSelectOnBoarding) return null
105122

106-
val nextOnBoardingPageInfo = onBoardingPageInfos[currentState.currentStep]
123+
val nextOnBoardingPageInfo = selectOnBoardingPageInfos[currentState.currentStep]
107124
val nextButtonEnable = nextOnBoardingPageInfo.isItemSelected
108125
return currentState.copy(
109126
currentOnBoardingPageInfo = nextOnBoardingPageInfo,
@@ -114,27 +131,35 @@ class OnBoardingViewModel @AssistedInject constructor(
114131

115132
is OnBoardingIntent.SelectPrevious -> {
116133
val currentState = state
117-
if (currentState !is OnBoardingState.Idle || currentState.currentStep == 1) {
134+
if (currentState !is OnBoardingState.Idle || currentState.currentStep == 0) {
118135
sendSideEffect(sideEffect = OnBoardingSideEffect.MoveToPreviousScreen)
119136
return null
120137
}
121138

122-
val isSelectOnBoardingStep = currentState.currentStep <= onBoardingPageInfos.size
139+
if (currentState.currentStep == 1) {
140+
return currentState.copy(
141+
currentStep = 0,
142+
currentOnBoardingPageInfo = OnBoardingPageInfo.Intro,
143+
nextButtonEnable = true
144+
)
145+
}
146+
147+
val isSelectOnBoardingStep = currentState.currentStep <= selectOnBoardingPageInfos.size
123148
if (isSelectOnBoardingStep) {
124-
val previousOnBoardingPageInfo = onBoardingPageInfos[currentState.currentStep - 2]
149+
val previousOnBoardingPageInfo = selectOnBoardingPageInfos[currentState.currentStep - 2]
125150
val nextButtonEnable = previousOnBoardingPageInfo.isItemSelected
126151
return currentState.copy(
127152
currentOnBoardingPageInfo = previousOnBoardingPageInfo,
128153
nextButtonEnable = nextButtonEnable,
129154
currentStep = currentState.currentStep - 1,
130155
)
131156
} else {
132-
val selectOnBoardingPageInfo = onBoardingPageInfos.last()
157+
val selectOnBoardingPageInfo = selectOnBoardingPageInfos.last()
133158
val nextButtonEnable = selectOnBoardingPageInfo.isItemSelected
134159
return currentState.copy(
135160
currentOnBoardingPageInfo = selectOnBoardingPageInfo,
136161
nextButtonEnable = nextButtonEnable,
137-
currentStep = onBoardingPageInfos.size,
162+
currentStep = selectOnBoardingPageInfos.size,
138163
)
139164
}
140165
}
@@ -193,9 +218,9 @@ class OnBoardingViewModel @AssistedInject constructor(
193218
val currentState = stateFlow.value
194219
if (currentState !is OnBoardingState.Idle) return@launch
195220

196-
val isLastSelectOnBoarding = currentState.currentStep >= onBoardingPageInfos.size
221+
val isLastSelectOnBoarding = currentState.currentStep >= selectOnBoardingPageInfos.size
197222
if (isLastSelectOnBoarding) {
198-
val selectedItemIdsWithOnBoardingId = getSelectedOnBoardingItemIdsWithId(onBoardingPageInfos)
223+
val selectedItemIdsWithOnBoardingId = getSelectedOnBoardingItemIdsWithId(selectOnBoardingPageInfos)
199224

200225
val onBoardingAbstract = getOnBoardingAbstractUseCase(selectedItemIdsWithOnBoardingId = selectedItemIdsWithOnBoardingId)
201226

@@ -239,12 +264,9 @@ class OnBoardingViewModel @AssistedInject constructor(
239264
}
240265

241266
fun loadRecommendRoutines() {
267+
loadRecommendRoutinesJob?.cancel()
242268
loadRecommendRoutinesJob = viewModelScope.async {
243-
val minimumDelayDeferred = async {
244-
delay(2000L)
245-
}
246-
247-
val selectedItems = onBoardingPageInfos
269+
val selectedItems = selectOnBoardingPageInfos
248270
.map { onBoardingPage ->
249271
val id = onBoardingPage.id
250272
val selectedItemIds = onBoardingPage.items.filter { onBoardingItem ->
@@ -259,7 +281,6 @@ class OnBoardingViewModel @AssistedInject constructor(
259281

260282
getRecommendOnBoardingRoutinesUseCase(selectedItems).fold(
261283
onSuccess = { recommendRoutines ->
262-
minimumDelayDeferred.await()
263284
if (isActive) {
264285
sendIntent(
265286
intent = OnBoardingIntent.LoadRecommendRoutinesSuccess(

0 commit comments

Comments
 (0)