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
26 changes: 24 additions & 2 deletions app/src/main/java/com/threegap/bitnagil/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import com.threegap.bitnagil.designsystem.BitnagilTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.threegap.bitnagil.designsystem.component.atom.BitnagilToastContainer
import com.threegap.bitnagil.designsystem.component.atom.rememberBitnagilToast
import com.threegap.bitnagil.presentation.common.toast.GlobalBitnagilToast
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
Expand All @@ -14,10 +23,23 @@ class MainActivity : ComponentActivity() {
enableEdgeToEdge()
setContent {
val mainNavigator = rememberMainNavigator()
BitnagilTheme {
val globalToast = rememberBitnagilToast()

LaunchedEffect(globalToast) {
GlobalBitnagilToast.initialize(globalToast)
}

Box(modifier = Modifier.fillMaxSize()) {
MainScreen(
navigator = mainNavigator,
)

BitnagilToastContainer(
state = globalToast,
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 100.dp),
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.threegap.bitnagil.designsystem.R
import com.threegap.bitnagil.designsystem.component.atom.BitnagilFloatingActionMenu
import com.threegap.bitnagil.designsystem.component.atom.FloatingActionItem
import com.threegap.bitnagil.designsystem.modifier.clickableWithoutRipple
import com.threegap.bitnagil.presentation.common.toast.GlobalBitnagilToast
import com.threegap.bitnagil.presentation.home.HomeScreenContainer
import com.threegap.bitnagil.presentation.mypage.MyPageScreenContainer
import com.threegap.bitnagil.presentation.recommendroutine.RecommendRoutineScreenContainer
Expand Down Expand Up @@ -114,8 +115,7 @@ fun HomeNavHost(
onToggle = { expanded -> showFloatingOverlay = expanded },
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(16.dp)
.padding(bottom = 80.dp),
.padding(end = 16.dp, bottom = 80.dp),
)
}
}
Expand All @@ -131,6 +131,7 @@ fun DoubleBackButtonPressedHandler() {
(context as? Activity)?.finish()
} else {
backPressedTimeMillis = System.currentTimeMillis()
GlobalBitnagilToast.showWarning("버튼을 한 번 더 누르면 종료됩니다.")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package com.threegap.bitnagil.designsystem.component.atom

import androidx.annotation.DrawableRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
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.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.threegap.bitnagil.designsystem.BitnagilTheme
import com.threegap.bitnagil.designsystem.R
import kotlinx.coroutines.delay

@Composable
fun BitnagilToastMessage(
text: String,
modifier: Modifier = Modifier,
@DrawableRes id: Int? = null,
) {
Box(
modifier = modifier
.background(
color = BitnagilTheme.colors.navy400,
shape = RoundedCornerShape(8.dp),
)
.padding(vertical = 10.dp, horizontal = 20.dp),
contentAlignment = Alignment.Center,
) {
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,
)
}
}
}

@Composable
fun BitnagilToastContainer(
state: BitnagilToastState,
modifier: Modifier = Modifier,
duration: Long = 2000L,
) {
if (state.isVisible) {
LaunchedEffect(state.toastId) {
if (state.isVisible) {
delay(duration)
state.hide()
}
}

AnimatedVisibility(
visible = state.isVisible,
enter = fadeIn() + slideInVertically { it / 2 },
exit = fadeOut() + slideOutVertically { it / 2 },
modifier = modifier,
) {
BitnagilToastMessage(
text = state.text,
id = state.icon,
)
}
}
}

class BitnagilToastState {
private var _text by mutableStateOf("")
private var _icon by mutableStateOf<Int?>(null)
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 isVisible: Boolean get() = _isVisible
val toastId: Int get() = _toastId

fun show(text: String, icon: Int? = null) {
if (shouldPreventDuplicateShow(text, icon)) {
return
}
showToast(text, icon)
}

fun hide() {
_isVisible = false
}

private fun shouldPreventDuplicateShow(text: String, icon: Int?): Boolean {
val currentTime = System.currentTimeMillis()
return _text == text && _icon == icon && currentTime - _lastShowTime < 500L
}

private fun showToast(text: String, icon: Int?) {
_text = text
_icon = icon
_isVisible = true
_lastShowTime = System.currentTimeMillis()
_toastId += 1
}
}

@Composable
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다시 시도해 주세요.",
)
}
}
18 changes: 18 additions & 0 deletions core/designsystem/src/main/res/drawable/ic_modal_warning.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="55dp"
android:height="55dp"
android:viewportWidth="55"
android:viewportHeight="55">
<group>
<clip-path android:pathData="M0,0h55v55h-55z" />
<path
android:fillColor="#C2C4C8"
android:pathData="M0.17,27.508C0.17,27.137 0.394,26.782 0.409,26.411C0.425,26.04 0.432,25.685 0.463,25.315C0.494,24.944 0.417,24.573 0.463,24.21C0.509,23.847 0.679,23.5 0.733,23.145C0.787,22.789 0.757,22.403 0.826,22.048C0.895,21.693 1.019,21.345 1.104,20.99C1.189,20.635 1.019,20.21 1.119,19.855C1.22,19.499 1.266,19.121 1.382,18.774C1.498,18.426 1.752,18.117 1.876,17.777C1.999,17.438 2.239,17.129 2.378,16.789C2.517,16.449 2.486,16.024 2.64,15.692C2.794,15.36 3.057,15.074 3.227,14.75C3.397,14.426 3.466,14.04 3.651,13.715C3.837,13.391 4.045,13.09 4.246,12.773C4.446,12.457 4.632,12.14 4.84,11.839C5.048,11.538 5.272,11.229 5.488,10.943C5.705,10.657 6.206,10.572 6.438,10.286C6.669,10.001 6.932,9.754 7.179,9.476C7.426,9.198 7.658,8.927 7.912,8.657C8.167,8.387 8.182,7.892 8.453,7.638C8.723,7.383 9.024,7.167 9.302,6.919C9.58,6.672 9.811,6.379 10.097,6.147C10.382,5.915 10.676,5.684 10.969,5.468C11.262,5.251 11.602,5.081 11.911,4.873C12.22,4.664 12.49,4.41 12.806,4.217C13.123,4.023 13.462,3.869 13.779,3.684C14.095,3.498 14.458,3.382 14.783,3.213C15.107,3.043 15.369,2.742 15.701,2.579C16.033,2.417 16.488,2.533 16.828,2.394C17.168,2.255 17.5,2.101 17.839,1.969C18.179,1.838 18.557,1.823 18.912,1.707C19.267,1.591 19.514,1.174 19.869,1.066C20.225,0.958 20.58,0.857 20.935,0.772C21.29,0.687 21.66,0.641 22.023,0.571C22.386,0.502 22.749,0.425 23.112,0.371C23.474,0.317 23.853,0.363 24.223,0.317C24.594,0.27 24.941,0.147 25.312,0.116C25.682,0.085 26.045,0.031 26.416,0.015C26.786,0 27.156,-0.008 27.519,-0.008C27.882,-0.008 28.253,0.255 28.615,0.27C28.978,0.286 29.341,0.348 29.704,0.378C30.067,0.409 30.453,0.263 30.816,0.309C31.178,0.355 31.526,0.517 31.888,0.571C32.251,0.626 32.599,0.765 32.954,0.834C33.309,0.904 33.672,0.958 34.027,1.043C34.382,1.127 34.76,1.151 35.107,1.251C35.455,1.351 35.879,1.266 36.227,1.382C36.574,1.498 36.906,1.676 37.253,1.807C37.601,1.938 37.879,2.224 38.218,2.371C38.558,2.518 38.905,2.626 39.237,2.78C39.569,2.935 39.801,3.29 40.125,3.46C40.449,3.63 40.827,3.691 41.144,3.877C41.46,4.062 41.908,4.031 42.217,4.232C42.526,4.433 42.641,4.919 42.943,5.128C43.243,5.336 43.768,5.236 44.062,5.46C44.355,5.684 44.509,6.085 44.795,6.317C45.081,6.549 45.32,6.834 45.59,7.082C45.86,7.329 46.239,7.46 46.501,7.715C46.764,7.97 47.003,8.255 47.258,8.518C47.512,8.781 47.628,9.174 47.875,9.445C48.122,9.715 48.423,9.939 48.655,10.225C48.886,10.51 49.218,10.711 49.442,11.012C49.666,11.314 49.705,11.723 49.921,12.032C50.137,12.341 50.585,12.472 50.785,12.781C50.986,13.09 50.955,13.538 51.14,13.862C51.326,14.186 51.604,14.441 51.773,14.773C51.943,15.105 52.105,15.43 52.26,15.762C52.414,16.094 52.707,16.372 52.854,16.712C53.001,17.051 52.885,17.492 53.016,17.839C53.147,18.187 53.124,18.573 53.24,18.92C53.356,19.268 53.595,19.569 53.695,19.924C53.796,20.279 53.873,20.635 53.958,20.99C54.043,21.345 54.236,21.677 54.305,22.04C54.375,22.403 54.498,22.751 54.552,23.114C54.606,23.477 54.622,23.847 54.668,24.21C54.714,24.573 54.792,24.936 54.815,25.307C54.838,25.677 54.946,26.04 54.961,26.403C54.977,26.766 54.815,27.145 54.815,27.515C54.815,27.886 54.961,28.257 54.946,28.62C54.931,28.983 54.691,29.346 54.66,29.709C54.63,30.072 54.545,30.435 54.498,30.798C54.452,31.16 54.56,31.547 54.498,31.91C54.437,32.272 54.56,32.674 54.483,33.029C54.406,33.385 54.22,33.732 54.136,34.087C54.05,34.443 53.75,34.744 53.649,35.099C53.549,35.454 53.68,35.879 53.564,36.227C53.448,36.574 53.302,36.922 53.171,37.261C53.039,37.601 52.653,37.848 52.507,38.18C52.36,38.512 52.314,38.898 52.159,39.231C52.005,39.563 51.866,39.902 51.696,40.227C51.526,40.551 51.187,40.775 51.001,41.099C50.816,41.424 50.785,41.825 50.585,42.134C50.384,42.443 50.098,42.69 49.89,42.991C49.681,43.293 49.512,43.617 49.288,43.91C49.064,44.204 48.894,44.536 48.662,44.822C48.431,45.107 48.215,45.409 47.975,45.687C47.736,45.965 47.543,46.297 47.288,46.559C47.034,46.822 46.771,47.084 46.509,47.339C46.246,47.594 45.953,47.826 45.683,48.073C45.413,48.32 45.05,48.467 44.764,48.698C44.479,48.93 44.178,49.139 43.884,49.355C43.591,49.571 43.228,49.695 42.927,49.903C42.626,50.112 42.294,50.266 41.985,50.459C41.676,50.652 41.507,51.085 41.19,51.262C40.874,51.44 40.426,51.386 40.102,51.556C39.778,51.726 39.453,51.88 39.121,52.042C38.79,52.204 38.566,52.583 38.226,52.73C37.886,52.876 37.508,52.915 37.168,53.046C36.829,53.178 36.543,53.479 36.196,53.595C35.848,53.71 35.509,53.865 35.154,53.965C34.799,54.066 34.374,53.88 34.011,53.965C33.648,54.05 33.324,54.228 32.961,54.297C32.599,54.367 32.22,54.297 31.858,54.359C31.495,54.421 31.14,54.467 30.769,54.514C30.399,54.56 30.051,54.691 29.688,54.722C29.326,54.753 28.963,54.931 28.6,54.938C28.237,54.946 27.867,55 27.496,55C27.126,55 26.755,54.931 26.392,54.915C26.029,54.9 25.651,54.961 25.281,54.931C24.91,54.9 24.578,54.56 24.215,54.514C23.853,54.467 23.482,54.467 23.119,54.413C22.757,54.359 22.347,54.537 21.992,54.459C21.637,54.382 21.267,54.32 20.904,54.236C20.541,54.151 20.186,54.035 19.839,53.934C19.491,53.834 19.128,53.71 18.781,53.595C18.434,53.479 18.125,53.231 17.785,53.1C17.446,52.969 17.098,52.83 16.766,52.691C16.434,52.552 16.056,52.475 15.724,52.313C15.392,52.15 15.207,51.71 14.883,51.54C14.559,51.37 14.057,51.517 13.74,51.332C13.424,51.146 13.092,50.961 12.783,50.768C12.474,50.575 12.142,50.39 11.841,50.181C11.54,49.973 11.378,49.563 11.085,49.347C10.792,49.131 10.421,49 10.135,48.768C9.85,48.536 9.749,48.096 9.472,47.849C9.194,47.602 8.869,47.432 8.599,47.177C8.329,46.922 7.989,46.752 7.735,46.49C7.48,46.227 7.241,45.949 6.994,45.671C6.747,45.393 6.585,45.053 6.353,44.768C6.121,44.482 5.905,44.188 5.681,43.895C5.458,43.602 5.265,43.3 5.048,42.991C4.832,42.682 4.516,42.466 4.315,42.157C4.114,41.848 4.13,41.416 3.945,41.099C3.759,40.783 3.481,40.52 3.312,40.196C3.142,39.871 2.926,39.57 2.771,39.238C2.617,38.906 2.385,38.605 2.239,38.265C2.092,37.925 2.069,37.539 1.938,37.2C1.806,36.86 1.606,36.535 1.49,36.188C1.374,35.84 1.235,35.493 1.135,35.138C1.034,34.782 1.181,34.365 1.096,34.01C1.011,33.655 0.942,33.3 0.872,32.937C0.803,32.574 0.757,32.218 0.695,31.855C0.633,31.493 0.309,31.168 0.262,30.805C0.216,30.442 0.069,30.079 0.039,29.716C0.008,29.353 -0.023,28.975 -0.039,28.612C-0.054,28.249 0.131,27.871 0.131,27.5L0.17,27.508Z" />
<path
android:fillColor="#ffffff"
android:pathData="M27.512,33.485V33.462C27.072,33.462 26.755,33.13 26.392,32.929C26.029,32.728 25.512,32.659 25.288,32.296C25.065,31.933 25.173,31.469 25.149,31.029C25.126,30.465 25.165,30.465 25.134,29.902C25.103,29.338 25.026,29.346 25.003,28.782C24.98,28.218 25.018,28.218 24.995,27.654C24.972,27.091 24.825,27.098 24.802,26.535C24.779,25.971 24.64,25.979 24.617,25.415C24.594,24.851 24.617,24.851 24.594,24.287C24.57,23.724 24.648,23.724 24.617,23.152C24.586,22.581 24.709,22.581 24.686,22.017C24.663,21.453 24.524,21.461 24.501,20.897C24.478,20.333 24.517,20.333 24.501,19.77C24.486,19.206 24.416,19.206 24.393,18.642C24.37,18.079 24.47,18.071 24.447,17.507C24.424,16.943 24.563,16.936 24.54,16.372C24.517,15.808 24.501,15.808 24.478,15.237C24.462,14.889 24.455,14.596 24.563,14.294C24.671,13.993 24.779,13.661 25.003,13.414C25.227,13.167 25.582,13.144 25.898,13.012C26.215,12.881 26.454,12.572 26.801,12.572C27.535,12.572 27.535,12.541 28.26,12.541C28.608,12.541 28.963,12.649 29.264,12.781C29.565,12.912 29.688,13.306 29.92,13.545C30.152,13.785 30.453,13.947 30.568,14.264C30.684,14.58 30.553,14.889 30.538,15.237C30.514,15.8 30.684,15.808 30.653,16.372C30.622,16.936 30.707,16.936 30.684,17.499C30.661,18.063 30.522,18.055 30.499,18.619C30.476,19.183 30.7,19.191 30.677,19.754C30.653,20.318 30.707,20.318 30.677,20.882C30.646,21.445 30.576,21.438 30.553,22.002C30.53,22.565 30.213,22.55 30.182,23.114C30.152,23.677 30.167,23.677 30.136,24.241C30.105,24.805 30.36,24.82 30.329,25.384C30.298,25.948 30.128,25.94 30.105,26.504C30.082,27.067 30.159,27.067 30.128,27.631C30.097,28.195 30.044,28.195 30.02,28.759C29.997,29.323 30.206,29.338 30.175,29.902C30.144,30.465 29.835,30.45 29.804,31.021C29.781,31.462 29.881,31.902 29.665,32.249C29.449,32.597 29.001,32.728 28.631,32.937C28.26,33.145 27.944,33.477 27.504,33.477L27.512,33.485Z" />
<path
android:fillColor="#ffffff"
android:pathData="M24.902,39.717C24.902,39.362 24.949,39.014 25.08,38.705C25.211,38.396 25.396,38.072 25.636,37.833C25.875,37.593 26.184,37.408 26.508,37.277C26.832,37.146 27.156,37.053 27.519,37.053C27.882,37.053 28.222,37.13 28.538,37.261C28.855,37.393 29.109,37.64 29.356,37.879C29.604,38.118 29.858,38.366 29.989,38.69C30.121,39.014 30.128,39.354 30.128,39.717C30.128,40.08 30.051,40.404 29.92,40.713C29.789,41.022 29.611,41.331 29.372,41.57C29.133,41.81 28.839,41.995 28.515,42.126C28.191,42.258 27.874,42.358 27.519,42.358C27.164,42.358 26.801,42.32 26.493,42.188C26.184,42.057 25.875,41.848 25.628,41.601C25.381,41.354 25.196,41.053 25.065,40.729C24.933,40.404 24.902,40.072 24.902,39.709V39.717Z" />
</group>
</vector>
Loading