Skip to content

Commit aeecfb9

Browse files
committed
Feat: BitnagilToastMessage 컴포넌트 구현
1 parent aea1f69 commit aeecfb9

1 file changed

Lines changed: 154 additions & 0 deletions

File tree

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package com.threegap.bitnagil.designsystem.component.atom
2+
3+
import androidx.annotation.DrawableRes
4+
import androidx.compose.animation.AnimatedVisibility
5+
import androidx.compose.animation.fadeIn
6+
import androidx.compose.animation.fadeOut
7+
import androidx.compose.animation.slideInVertically
8+
import androidx.compose.animation.slideOutVertically
9+
import androidx.compose.foundation.background
10+
import androidx.compose.foundation.layout.Arrangement
11+
import androidx.compose.foundation.layout.Box
12+
import androidx.compose.foundation.layout.Column
13+
import androidx.compose.foundation.layout.Row
14+
import androidx.compose.foundation.layout.padding
15+
import androidx.compose.foundation.layout.size
16+
import androidx.compose.foundation.shape.RoundedCornerShape
17+
import androidx.compose.material3.Text
18+
import androidx.compose.runtime.Composable
19+
import androidx.compose.runtime.LaunchedEffect
20+
import androidx.compose.runtime.getValue
21+
import androidx.compose.runtime.mutableIntStateOf
22+
import androidx.compose.runtime.mutableStateOf
23+
import androidx.compose.runtime.remember
24+
import androidx.compose.runtime.setValue
25+
import androidx.compose.ui.Alignment
26+
import androidx.compose.ui.Modifier
27+
import androidx.compose.ui.text.style.TextAlign
28+
import androidx.compose.ui.tooling.preview.Preview
29+
import androidx.compose.ui.unit.dp
30+
import com.threegap.bitnagil.designsystem.BitnagilTheme
31+
import com.threegap.bitnagil.designsystem.R
32+
import kotlinx.coroutines.delay
33+
34+
@Composable
35+
fun BitnagilToastMessage(
36+
text: String,
37+
modifier: Modifier = Modifier,
38+
@DrawableRes id: Int? = null,
39+
) {
40+
Box(
41+
modifier = modifier
42+
.background(
43+
color = BitnagilTheme.colors.navy400,
44+
shape = RoundedCornerShape(8.dp),
45+
)
46+
.padding(vertical = 10.dp, horizontal = 20.dp),
47+
contentAlignment = Alignment.Center,
48+
) {
49+
Row(
50+
verticalAlignment = Alignment.CenterVertically,
51+
) {
52+
if (id != null) {
53+
BitnagilIcon(
54+
id = id,
55+
tint = BitnagilTheme.colors.error,
56+
modifier = Modifier
57+
.padding(end = 4.dp)
58+
.size(24.dp),
59+
)
60+
}
61+
Text(
62+
text = text,
63+
color = BitnagilTheme.colors.white,
64+
style = BitnagilTheme.typography.body2Medium,
65+
textAlign = TextAlign.Center,
66+
)
67+
}
68+
}
69+
}
70+
71+
@Composable
72+
fun BitnagilToastContainer(
73+
state: BitnagilToastState,
74+
modifier: Modifier = Modifier,
75+
duration: Long = 2000L,
76+
) {
77+
if (state.isVisible) {
78+
LaunchedEffect(state.toastId) {
79+
if (state.isVisible) {
80+
delay(duration)
81+
state.hide()
82+
}
83+
}
84+
85+
AnimatedVisibility(
86+
visible = state.isVisible,
87+
enter = fadeIn() + slideInVertically { it / 2 },
88+
exit = fadeOut() + slideOutVertically { it / 2 },
89+
modifier = modifier,
90+
) {
91+
BitnagilToastMessage(
92+
text = state.text,
93+
id = state.icon,
94+
)
95+
}
96+
}
97+
}
98+
99+
class BitnagilToastState {
100+
private var _text by mutableStateOf("")
101+
private var _icon by mutableStateOf<Int?>(null)
102+
private var _isVisible by mutableStateOf(false)
103+
private var _toastId by mutableIntStateOf(0)
104+
private var _lastShowTime = 0L
105+
106+
val text: String get() = _text
107+
val icon: Int? get() = _icon
108+
val isVisible: Boolean get() = _isVisible
109+
val toastId: Int get() = _toastId
110+
111+
fun show(text: String, icon: Int? = null) {
112+
if (shouldPreventDuplicateShow(text, icon)) {
113+
return
114+
}
115+
showToast(text, icon)
116+
}
117+
118+
fun hide() {
119+
_isVisible = false
120+
}
121+
122+
private fun shouldPreventDuplicateShow(text: String, icon: Int?): Boolean {
123+
val currentTime = System.currentTimeMillis()
124+
return _text == text && _icon == icon && currentTime - _lastShowTime < 500L
125+
}
126+
127+
private fun showToast(text: String, icon: Int?) {
128+
_text = text
129+
_icon = icon
130+
_isVisible = true
131+
_lastShowTime = System.currentTimeMillis()
132+
_toastId += 1
133+
}
134+
}
135+
136+
@Composable
137+
fun rememberBitnagilToast(): BitnagilToastState = remember { BitnagilToastState() }
138+
139+
@Preview
140+
@Composable
141+
private fun BitnagilToastMessagePreview() {
142+
Column(
143+
verticalArrangement = Arrangement.spacedBy(8.dp),
144+
) {
145+
BitnagilToastMessage(
146+
text = "버튼을 한 번 더 누르면 종료됩니다.",
147+
id = R.drawable.ic_warning,
148+
)
149+
150+
BitnagilToastMessage(
151+
text = "루틴 완료 상태 저장에 실패했어요.\n다시 시도해 주세요.",
152+
)
153+
}
154+
}

0 commit comments

Comments
 (0)