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
4 changes: 3 additions & 1 deletion app/src/main/java/com/threegap/bitnagil/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
Expand Down Expand Up @@ -53,7 +54,8 @@ class MainActivity : ComponentActivity() {
state = globalToast,
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 100.dp),
.navigationBarsPadding()
.padding(bottom = if (mainNavigator.hasBottomNavigationBarRoute()) 80.dp else 16.dp),
)
}
}
Expand Down
19 changes: 19 additions & 0 deletions app/src/main/java/com/threegap/bitnagil/MainNavigator.kt
Original file line number Diff line number Diff line change
@@ -1,21 +1,40 @@
package com.threegap.bitnagil

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hasRoute
import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController

class MainNavigator(
val navController: NavHostController,
) {
val startDestination = Route.Splash

private val currentDestination: NavDestination?
@Composable get() {
val currentEntry by navController.currentBackStackEntryAsState()
return currentEntry?.destination
}

internal fun navigateToHomeAndClearStack() =
navController.navigate(Route.Home) {
popUpTo(0) {
inclusive = true
}
}

@Composable
internal fun hasBottomNavigationBarRoute(): Boolean {
val destination = currentDestination
return when {
destination?.hasRoute<Route.Home>() == true -> true
else -> false
}
}
}

@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
Expand All @@ -33,38 +32,34 @@ import kotlinx.coroutines.delay

@Composable
fun BitnagilToastMessage(
@DrawableRes id: Int,
text: String,
modifier: Modifier = Modifier,
@DrawableRes id: Int? = null,
) {
Box(
Row(
modifier = modifier
.padding(horizontal = 16.dp)
.background(
color = BitnagilTheme.colors.navy400,
shape = RoundedCornerShape(8.dp),
color = BitnagilTheme.colors.coolGray30,
shape = RoundedCornerShape(12.dp),
)
.padding(vertical = 10.dp, horizontal = 20.dp),
contentAlignment = Alignment.Center,
.fillMaxWidth()
.padding(vertical = 14.dp, horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
) {
if (id != null) {
BitnagilIcon(
id = id,
tint = BitnagilTheme.colors.error,
modifier = Modifier
.padding(end = 4.dp)
.size(24.dp),
)
}
Text(
text = text,
color = BitnagilTheme.colors.white,
style = BitnagilTheme.typography.body2Medium,
textAlign = TextAlign.Center,
)
}
BitnagilIcon(
id = id,
tint = null,
modifier = Modifier.size(24.dp),
)

Text(
text = text,
color = BitnagilTheme.colors.white,
style = BitnagilTheme.typography.body2Medium,
textAlign = TextAlign.Center,
)
}
}

Expand Down Expand Up @@ -98,17 +93,17 @@ fun BitnagilToastContainer(

class BitnagilToastState {
private var _text by mutableStateOf("")
private var _icon by mutableStateOf<Int?>(null)
private var _icon by mutableIntStateOf(0)
private var _isVisible by mutableStateOf(false)
private var _toastId by mutableIntStateOf(0)
private var _lastShowTime = 0L

val text: String get() = _text
val icon: Int? get() = _icon
val icon: Int get() = _icon
val isVisible: Boolean get() = _isVisible
val toastId: Int get() = _toastId

fun show(text: String, icon: Int? = null) {
fun show(text: String, icon: Int) {
if (shouldPreventDuplicateShow(text, icon)) {
return
}
Expand All @@ -124,7 +119,7 @@ class BitnagilToastState {
return _text == text && _icon == icon && currentTime - _lastShowTime < 500L
}

private fun showToast(text: String, icon: Int?) {
private fun showToast(text: String, icon: Int) {
_text = text
_icon = icon
_isVisible = true
Expand All @@ -139,16 +134,8 @@ fun rememberBitnagilToast(): BitnagilToastState = remember { BitnagilToastState(
@Preview
@Composable
private fun BitnagilToastMessagePreview() {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
BitnagilToastMessage(
text = "버튼을 한 번 더 누르면 종료됩니다.",
id = R.drawable.ic_warning,
)

BitnagilToastMessage(
text = "루틴 완료 상태 저장에 실패했어요.\n다시 시도해 주세요.",
)
}
BitnagilToastMessage(
text = "버튼을 한 번 더 누르면 종료됩니다.",
id = R.drawable.ic_warning,
)
}
27 changes: 24 additions & 3 deletions core/designsystem/src/main/res/drawable/ic_warning.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,29 @@
android:viewportHeight="24">
<path
android:fillColor="#FF6868"
android:pathData="M12,15.5C11.575,15.5 11.219,15.383 10.931,15.149C10.644,14.914 10.5,14.624 10.5,14.278V5.722C10.5,5.376 10.644,5.086 10.931,4.851C11.219,4.617 11.575,4.5 12,4.5C12.425,4.5 12.781,4.617 13.069,4.851C13.356,5.086 13.5,5.376 13.5,5.722V14.278C13.5,14.624 13.356,14.914 13.069,15.149C12.781,15.383 12.425,15.5 12,15.5Z" />
android:pathData="M4.622,15.21C4.472,15.47 4.332,15.71 4.202,15.93C4.052,16.2 3.632,16.28 3.512,16.5C3.362,16.79 3.132,17 3.052,17.22C2.942,17.53 2.892,17.87 2.912,18.11C2.942,18.39 3.002,18.76 3.142,19C3.282,19.24 3.612,19.41 3.842,19.58C4.032,19.72 4.372,19.55 4.702,19.61C4.932,19.65 5.172,19.78 5.492,19.79C5.742,19.8 6.012,19.8 6.312,19.8C6.562,19.8 6.842,19.63 7.142,19.63C7.542,19.63 7.542,19.79 7.942,19.79C8.342,19.79 8.342,19.88 8.752,19.88C9.162,19.88 9.152,20.12 9.562,20.12C9.972,20.12 9.962,20.12 10.372,20.12C10.781,20.12 10.771,19.79 11.182,19.79C11.592,19.79 11.582,19.9 11.991,19.9C12.401,19.9 12.392,19.99 12.802,19.99C13.212,19.99 13.201,20.09 13.611,20.09C14.021,20.09 14.012,19.74 14.422,19.74C14.832,19.74 14.832,20.03 15.231,20.03C15.632,20.03 15.642,20.04 16.042,20.04C16.441,20.04 16.451,19.77 16.851,19.77C17.152,19.77 17.431,19.6 17.681,19.59C17.992,19.59 18.271,19.87 18.511,19.85C18.831,19.84 19.111,19.85 19.341,19.81C19.671,19.75 19.751,19.41 19.941,19.27C20.171,19.1 20.601,19.16 20.742,18.92C20.882,18.68 21.031,18.38 21.062,18.1C21.091,17.86 21.132,17.5 21.011,17.19C20.931,16.97 20.581,16.81 20.431,16.52C20.322,16.3 20.132,16.09 19.982,15.82C19.851,15.6 19.851,15.28 19.701,15.02C19.501,14.67 19.521,14.66 19.312,14.31C19.101,13.96 18.761,14.16 18.552,13.81C18.341,13.46 18.701,13.26 18.501,12.91C18.302,12.56 18.351,12.53 18.152,12.18C17.951,11.83 17.751,11.94 17.552,11.59C17.351,11.24 17.242,11.3 17.042,10.95C16.841,10.6 16.861,10.59 16.662,10.24C16.462,9.89 16.462,9.89 16.261,9.54C16.062,9.19 16.281,9.06 16.081,8.71C15.882,8.36 15.651,8.49 15.451,8.14C15.252,7.79 15.601,7.58 15.401,7.23C15.201,6.88 15.151,6.9 14.951,6.55C14.802,6.29 14.571,6.1 14.441,5.88C14.281,5.61 13.872,5.55 13.741,5.34C13.571,5.07 13.741,4.6 13.592,4.42C13.382,4.17 13.071,4.02 12.851,3.92C12.592,3.8 12.262,3.99 11.991,3.99C11.722,3.99 11.542,4.15 11.281,4.26C11.061,4.36 10.672,4.22 10.451,4.48C10.302,4.66 10.292,4.99 10.111,5.26C9.982,5.47 9.702,5.62 9.552,5.89C9.422,6.11 9.242,6.32 9.082,6.58C8.882,6.93 9.012,7 8.802,7.35C8.592,7.7 8.852,7.84 8.642,8.19C8.432,8.54 8.132,8.36 7.932,8.71C7.732,9.06 7.662,9.02 7.462,9.37C7.262,9.72 7.162,9.66 6.962,10.01C6.762,10.36 7.042,10.52 6.842,10.87C6.642,11.22 6.712,11.26 6.512,11.61C6.312,11.96 6.392,12.01 6.192,12.35C5.992,12.69 5.842,12.62 5.642,12.97C5.442,13.32 5.242,13.2 5.032,13.56C4.822,13.92 4.812,13.93 4.692,14.32C4.562,14.74 4.792,14.83 4.592,15.18L4.622,15.21Z" />
<path
android:fillColor="#FF6868"
android:pathData="M12,18m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0" />
android:fillColor="#00000000"
android:pathData="M4.622,15.21C4.472,15.47 4.332,15.71 4.202,15.93C4.052,16.2 3.632,16.28 3.512,16.5C3.362,16.79 3.132,17 3.052,17.22C2.942,17.53 2.892,17.87 2.912,18.11C2.942,18.39 3.002,18.76 3.142,19C3.282,19.24 3.612,19.41 3.842,19.58C4.032,19.72 4.372,19.55 4.702,19.61C4.932,19.65 5.172,19.78 5.492,19.79C5.742,19.8 6.012,19.8 6.312,19.8C6.562,19.8 6.842,19.63 7.142,19.63C7.542,19.63 7.542,19.79 7.942,19.79C8.342,19.79 8.342,19.88 8.752,19.88C9.162,19.88 9.152,20.12 9.562,20.12C9.972,20.12 9.962,20.12 10.372,20.12C10.781,20.12 10.771,19.79 11.182,19.79C11.592,19.79 11.582,19.9 11.991,19.9C12.401,19.9 12.392,19.99 12.802,19.99C13.212,19.99 13.201,20.09 13.611,20.09C14.021,20.09 14.012,19.74 14.422,19.74C14.832,19.74 14.832,20.03 15.231,20.03C15.632,20.03 15.642,20.04 16.042,20.04C16.441,20.04 16.451,19.77 16.851,19.77C17.152,19.77 17.431,19.6 17.681,19.59C17.992,19.59 18.271,19.87 18.511,19.85C18.831,19.84 19.111,19.85 19.341,19.81C19.671,19.75 19.751,19.41 19.941,19.27C20.171,19.1 20.601,19.16 20.742,18.92C20.882,18.68 21.031,18.38 21.062,18.1C21.091,17.86 21.132,17.5 21.011,17.19C20.931,16.97 20.581,16.81 20.431,16.52C20.322,16.3 20.132,16.09 19.982,15.82C19.851,15.6 19.851,15.28 19.701,15.02C19.501,14.67 19.521,14.66 19.312,14.31C19.101,13.96 18.761,14.16 18.552,13.81C18.341,13.46 18.701,13.26 18.501,12.91C18.302,12.56 18.351,12.53 18.152,12.18C17.951,11.83 17.751,11.94 17.552,11.59C17.351,11.24 17.242,11.3 17.042,10.95C16.841,10.6 16.861,10.59 16.662,10.24C16.462,9.89 16.462,9.89 16.261,9.54C16.062,9.19 16.281,9.06 16.081,8.71C15.882,8.36 15.651,8.49 15.451,8.14C15.252,7.79 15.601,7.58 15.401,7.23C15.201,6.88 15.151,6.9 14.951,6.55C14.802,6.29 14.571,6.1 14.441,5.88C14.281,5.61 13.872,5.55 13.741,5.34C13.571,5.07 13.741,4.6 13.592,4.42C13.382,4.17 13.071,4.02 12.851,3.92C12.592,3.8 12.262,3.99 11.991,3.99C11.722,3.99 11.542,4.15 11.281,4.26C11.061,4.36 10.672,4.22 10.451,4.48C10.302,4.66 10.292,4.99 10.111,5.26C9.982,5.47 9.702,5.62 9.552,5.89C9.422,6.11 9.242,6.32 9.082,6.58C8.882,6.93 9.012,7 8.802,7.35C8.592,7.7 8.852,7.84 8.642,8.19C8.432,8.54 8.132,8.36 7.932,8.71C7.732,9.06 7.662,9.02 7.462,9.37C7.262,9.72 7.162,9.66 6.962,10.01C6.762,10.36 7.042,10.52 6.842,10.87C6.642,11.22 6.712,11.26 6.512,11.61C6.312,11.96 6.392,12.01 6.192,12.35C5.992,12.69 5.842,12.62 5.642,12.97C5.442,13.32 5.242,13.2 5.032,13.56C4.822,13.92 4.812,13.93 4.692,14.32C4.562,14.74 4.792,14.83 4.592,15.18L4.622,15.21Z"
android:strokeWidth="2"
android:strokeColor="#FF6868"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M12.191,8.88C12.191,9.28 12.301,9.28 12.301,9.68C12.301,10.08 12.161,10.08 12.161,10.48C12.161,10.88 11.871,10.88 11.871,11.28C11.871,11.68 11.891,11.68 11.891,12.08C11.891,12.48 12.181,12.48 12.181,12.88"
android:strokeWidth="2"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M11.932,16.01C11.932,16.01 11.972,15.81 11.932,15.84C11.892,15.87 12.162,15.84 12.132,15.8C12.102,15.76 12.012,15.77 11.962,15.77C11.882,15.77 11.892,15.97 11.932,16.01Z"
android:strokeWidth="2"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#ffffff"
android:pathData="M11.954,15.889m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0" />
</vector>
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ enum class DayOfWeek {
}

fun List<DayOfWeek>.formatRepeatDays(): String {
if (this.isEmpty()) return "반복 없음"
if (this.isEmpty()) return "x"
return this.sortedBy { it.ordinal }
.joinToString(", ") { it.toKoreanShort() }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ object GlobalBitnagilToast {
_toastStateRef = WeakReference(toastState)
}

fun show(text: String, icon: Int? = null) {
fun show(text: String, icon: Int) {
_toastStateRef?.get()?.show(text, icon)
}

fun showCheck(text: String) = show(text, R.drawable.ic_check)
fun showCheck(text: String) = show(text, R.drawable.ic_check_circle_orange)

fun showWarning(text: String) = show(text, R.drawable.ic_warning)
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.threegap.bitnagil.designsystem.BitnagilTheme
import com.threegap.bitnagil.designsystem.modifier.clickableWithoutRipple
import com.threegap.bitnagil.presentation.common.flow.collectAsEffect
import com.threegap.bitnagil.presentation.common.toast.GlobalBitnagilToast
import com.threegap.bitnagil.presentation.home.component.template.CollapsibleHomeHeader
import com.threegap.bitnagil.presentation.home.component.template.EmptyRoutineView
import com.threegap.bitnagil.presentation.home.component.template.RoutineSection
Expand Down Expand Up @@ -64,14 +63,6 @@ fun HomeScreenContainer(
is HomeSideEffect.NavigateToRoutineList -> {
navigateToRoutineList(sideEffect.selectedDate)
}

is HomeSideEffect.ShowToastWithIcon -> {
GlobalBitnagilToast.showCheck(sideEffect.message)
}

is HomeSideEffect.ShowToast -> {
GlobalBitnagilToast.show(sideEffect.message)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ class HomeViewModel @Inject constructor(
}

is HomeIntent.RoutineToggleCompletionFailure -> {
sendSideEffect(HomeSideEffect.ShowToast("루틴 완료 상태 저장에 실패했어요.\n다시 시도해 주세요."))
null
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package com.threegap.bitnagil.presentation.home.model
import com.threegap.bitnagil.presentation.common.mviviewmodel.MviSideEffect

sealed interface HomeSideEffect : MviSideEffect {
data class ShowToast(val message: String) : HomeSideEffect
data class ShowToastWithIcon(val message: String) : HomeSideEffect
data object NavigateToGuide : HomeSideEffect
data object NavigateToRegisterRoutine : HomeSideEffect
data object NavigateToEmotion : HomeSideEffect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ private val koreanLocale = Locale.KOREAN
private val monthYearFormatter = DateTimeFormatter.ofPattern("yyyy년 M월", koreanLocale)
private val executionTimeFormatter12 = DateTimeFormatter.ofPattern("a h:mm", koreanLocale)
private val executionTimeFormatter24 = DateTimeFormatter.ofPattern("HH:mm", koreanLocale)
private val shortDateFormatter = DateTimeFormatter.ofPattern("yy.MM.dd")

fun LocalDate.getCurrentWeekDays(): List<LocalDate> =
(0..6).map { this.with(DayOfWeek.MONDAY).plusDays(it.toLong()) }
Expand Down Expand Up @@ -47,3 +48,11 @@ fun String.formatExecutionTime12Hour(): String =
} catch (e: Exception) {
"시간 미정"
}

fun String.toShortDateFormat(): String =
try {
val date = LocalDate.parse(this)
date.format(shortDateFormatter)
} catch (e: Exception) {
this
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.threegap.bitnagil.designsystem.BitnagilTheme
import com.threegap.bitnagil.designsystem.component.block.BitnagilTopBar
import com.threegap.bitnagil.presentation.common.flow.collectAsEffect
import com.threegap.bitnagil.presentation.common.toast.GlobalBitnagilToast
import com.threegap.bitnagil.presentation.routinelist.component.template.DeleteConfirmBottomSheet
import com.threegap.bitnagil.presentation.routinelist.component.template.EditConfirmBottomSheet
import com.threegap.bitnagil.presentation.routinelist.component.template.EmptyRoutineListView
Expand All @@ -44,10 +45,9 @@ fun RoutineListScreenContainer(

viewModel.sideEffectFlow.collectAsEffect { sideEffect ->
when (sideEffect) {
is RoutineListSideEffect.ShowToast -> GlobalBitnagilToast.showCheck(sideEffect.message)
is RoutineListSideEffect.NavigateToBack -> navigateToBack()
is RoutineListSideEffect.NavigateToAddRoutine -> {
navigateToAddRoutine()
}
is RoutineListSideEffect.NavigateToAddRoutine -> navigateToAddRoutine()
is RoutineListSideEffect.NavigateToEditRoutine -> {
navigateToEditRoutine(sideEffect.routineId, sideEffect.updateRoutineFromNowDate)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,7 @@ class RoutineListViewModel @Inject constructor(
}

is RoutineListIntent.OnRegisterRoutineClick -> {
sendSideEffect(
RoutineListSideEffect.NavigateToAddRoutine,
)
sendSideEffect(RoutineListSideEffect.NavigateToAddRoutine)
null
}

Expand Down Expand Up @@ -103,6 +101,14 @@ class RoutineListViewModel @Inject constructor(
}
null
}

is RoutineListIntent.OnSuccessDeletedRoutine -> {
sendSideEffect(RoutineListSideEffect.ShowToast("삭제가 완료되었습니다."))
state.copy(
isLoading = false,
deleteConfirmBottomSheetVisible = false,
)
}
}

return newState
Expand Down Expand Up @@ -133,8 +139,8 @@ class RoutineListViewModel @Inject constructor(
viewModelScope.launch {
deleteRoutineUseCase(selectedRoutine.routineId).fold(
onSuccess = {
sendIntent(RoutineListIntent.HideDeleteConfirmBottomSheet)
sendIntent(RoutineListIntent.UpdateLoading(false))
fetchRoutines()
sendIntent(RoutineListIntent.OnSuccessDeletedRoutine)
},
onFailure = {
Log.e("RoutineListViewModel", "루틴 삭제 실패: ${it.message}")
Expand All @@ -150,8 +156,8 @@ class RoutineListViewModel @Inject constructor(
viewModelScope.launch {
deleteRoutineForDayUseCase(selectedRoutine.routineId).fold(
onSuccess = {
sendIntent(RoutineListIntent.HideDeleteConfirmBottomSheet)
sendIntent(RoutineListIntent.UpdateLoading(false))
fetchRoutines()
sendIntent(RoutineListIntent.OnSuccessDeletedRoutine)
},
onFailure = {
Log.e("RoutineListViewModel", "루틴 삭제 실패: ${it.message}")
Expand Down
Loading