Skip to content

Commit e6662c3

Browse files
committed
[fix]: 충돌해결 (#37)
2 parents 5066a0e + 537ca9f commit e6662c3

53 files changed

Lines changed: 3468 additions & 93 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.idea/deviceManager.xml

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.texthip.thip.ui.common.alarmpage.component
2+
3+
import androidx.compose.foundation.layout.Arrangement
4+
import androidx.compose.foundation.layout.Row
5+
import androidx.compose.runtime.Composable
6+
import androidx.compose.runtime.getValue
7+
import androidx.compose.runtime.mutableStateOf
8+
import androidx.compose.runtime.remember
9+
import androidx.compose.runtime.setValue
10+
import androidx.compose.ui.res.stringResource
11+
import androidx.compose.ui.tooling.preview.Preview
12+
import androidx.compose.ui.unit.dp
13+
import com.texthip.thip.R
14+
import com.texthip.thip.ui.common.buttons.OptionChipButton
15+
16+
@Composable
17+
fun AlarmFilterRow(
18+
selectedStates: BooleanArray,
19+
onToggle: (Int) -> Unit
20+
) {
21+
Row(
22+
horizontalArrangement = Arrangement.spacedBy(12.dp)
23+
) {
24+
OptionChipButton(
25+
text = stringResource(R.string.alarm_feed),
26+
isFilled = true,
27+
onClick = { onToggle(0) }
28+
)
29+
OptionChipButton(
30+
text = stringResource(R.string.alarm_group),
31+
isFilled = true,
32+
onClick = { onToggle(1) }
33+
)
34+
}
35+
}
36+
37+
@Preview(showBackground = true)
38+
@Composable
39+
fun AlarmFilterRowPreview() {
40+
var selectedStates by remember { mutableStateOf(booleanArrayOf(false, false)) }
41+
42+
AlarmFilterRow(
43+
selectedStates = selectedStates,
44+
onToggle = { idx ->
45+
selectedStates = selectedStates.copyOf().also { it[idx] = !it[idx] }
46+
}
47+
)
48+
}
49+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.texthip.thip.ui.common.alarmpage.mock
2+
3+
data class AlarmItem(
4+
val id: Int,
5+
val badgeText: String,
6+
val title: String,
7+
val message: String,
8+
val timeAgo: String,
9+
var isRead: Boolean
10+
)
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package com.texthip.thip.ui.common.alarmpage.screen
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Arrangement
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.PaddingValues
7+
import androidx.compose.foundation.layout.Spacer
8+
import androidx.compose.foundation.layout.fillMaxSize
9+
import androidx.compose.foundation.layout.height
10+
import androidx.compose.foundation.layout.padding
11+
import androidx.compose.foundation.lazy.LazyColumn
12+
import androidx.compose.foundation.lazy.items
13+
import androidx.compose.material3.Icon
14+
import androidx.compose.material3.Text
15+
import androidx.compose.runtime.Composable
16+
import androidx.compose.runtime.getValue
17+
import androidx.compose.runtime.mutableStateOf
18+
import androidx.compose.runtime.remember
19+
import androidx.compose.runtime.setValue
20+
import androidx.compose.ui.Alignment
21+
import androidx.compose.ui.Modifier
22+
import androidx.compose.ui.res.painterResource
23+
import androidx.compose.ui.res.stringResource
24+
import androidx.compose.ui.tooling.preview.Preview
25+
import androidx.compose.ui.unit.dp
26+
import com.texthip.thip.R
27+
import com.texthip.thip.ui.common.alarmpage.component.AlarmFilterRow
28+
import com.texthip.thip.ui.common.alarmpage.mock.AlarmItem
29+
import com.texthip.thip.ui.common.cards.CardAlarm
30+
import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar
31+
import com.texthip.thip.ui.theme.ThipTheme
32+
import com.texthip.thip.ui.theme.ThipTheme.colors
33+
import com.texthip.thip.ui.theme.ThipTheme.typography
34+
35+
@Composable
36+
fun AlarmScreen(
37+
alarmItems: List<AlarmItem>, onCardClick: (AlarmItem) -> Unit = {}
38+
) {
39+
var selectedStates by remember { mutableStateOf(booleanArrayOf(false, false)) }
40+
var alarms by remember { mutableStateOf(alarmItems) }
41+
42+
val filteredList = when {
43+
selectedStates[0] && !selectedStates[1] -> alarms.filter { it.badgeText == stringResource(R.string.alarm_feed) }
44+
!selectedStates[0] && selectedStates[1] -> alarms.filter { it.badgeText == stringResource(R.string.alarm_group) }
45+
else -> alarms
46+
}
47+
48+
Column(
49+
Modifier
50+
.fillMaxSize()
51+
) {
52+
DefaultTopAppBar(
53+
title = stringResource(R.string.alarm_string),
54+
onLeftClick = {},
55+
)
56+
Column(
57+
Modifier
58+
.fillMaxSize()
59+
.padding(horizontal = 20.dp)
60+
) {
61+
Spacer(modifier = Modifier.height(20.dp))
62+
AlarmFilterRow(
63+
selectedStates = selectedStates, onToggle = { idx ->
64+
selectedStates = selectedStates.copyOf().also { it[idx] = !it[idx] }
65+
})
66+
Spacer(modifier = Modifier.height(20.dp))
67+
68+
if (filteredList.isEmpty()) {
69+
70+
Column(
71+
modifier = Modifier.fillMaxSize(),
72+
verticalArrangement = Arrangement.Center,
73+
horizontalAlignment = Alignment.CenterHorizontally
74+
) {
75+
Icon(
76+
painter = painterResource(R.drawable.ic_notification),
77+
contentDescription = null,
78+
tint = colors.Grey02,
79+
)
80+
Spacer(modifier = Modifier.height(12.dp))
81+
Text(
82+
text = stringResource(R.string.alarm_notification_comment),
83+
style = typography.smalltitle_sb600_s16_h20,
84+
color = colors.Grey01
85+
)
86+
}
87+
} else {
88+
LazyColumn(
89+
verticalArrangement = Arrangement.spacedBy(20.dp),
90+
contentPadding = PaddingValues(bottom = 20.dp),
91+
modifier = Modifier.fillMaxSize()
92+
) {
93+
items(filteredList, key = { it.id }) { alarm ->
94+
CardAlarm(
95+
badgeText = alarm.badgeText,
96+
title = alarm.title,
97+
message = alarm.message,
98+
timeAgo = alarm.timeAgo,
99+
isRead = alarm.isRead,
100+
onClick = {
101+
alarms = alarms.map {
102+
if (it.id == alarm.id) it.copy(isRead = true) else it
103+
}
104+
})
105+
}
106+
}
107+
}
108+
}
109+
}
110+
}
111+
112+
113+
@Preview(showBackground = true)
114+
@Composable
115+
fun AlarmScreenPreview() {
116+
ThipTheme {
117+
AlarmScreen(
118+
alarmItems = listOf(
119+
AlarmItem(1, "피드", "내 글을 좋아합니다.", "user123님이 내 글에 좋아요를 눌렀어요.", "2", false),
120+
AlarmItem(2, "모임", "같이 읽기를 시작했어요!", "모임방에서 20분 동안 같이 읽기가 시작되었어요!", "7", false),
121+
AlarmItem(3, "피드", "내 글에 댓글이 달렸어요.", "user1: 진짜 공감합니다!", "2025.01.12", true),
122+
AlarmItem(4, "모임", "투표가 시작되었어요!", "투표지를 먼저 열람합니다.", "17", false)
123+
)
124+
)
125+
}
126+
}
127+
128+
@Preview(showBackground = true)
129+
@Composable
130+
fun AlarmScreenEmptyPreview() {
131+
ThipTheme {
132+
AlarmScreen(
133+
alarmItems = emptyList()
134+
)
135+
}
136+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package com.texthip.thip.ui.common.bottomsheet
2+
3+
4+
import androidx.compose.animation.core.Animatable
5+
import androidx.compose.animation.core.tween
6+
import androidx.compose.foundation.background
7+
import androidx.compose.foundation.clickable
8+
import androidx.compose.foundation.gestures.detectVerticalDragGestures
9+
import androidx.compose.foundation.interaction.MutableInteractionSource
10+
import androidx.compose.foundation.layout.*
11+
import androidx.compose.foundation.shape.RoundedCornerShape
12+
import androidx.compose.material3.Button
13+
import androidx.compose.material3.Text
14+
import androidx.compose.runtime.*
15+
import androidx.compose.ui.Alignment
16+
import androidx.compose.ui.Modifier
17+
import androidx.compose.ui.graphics.Color
18+
import androidx.compose.ui.input.pointer.pointerInput
19+
import androidx.compose.ui.tooling.preview.Preview
20+
import androidx.compose.ui.unit.dp
21+
import androidx.compose.ui.zIndex
22+
import com.texthip.thip.ui.theme.ThipTheme
23+
import com.texthip.thip.ui.theme.ThipTheme.colors
24+
import kotlinx.coroutines.launch
25+
26+
@Composable
27+
fun CustomBottomSheet(
28+
onDismiss: () -> Unit,
29+
content: @Composable ColumnScope.() -> Unit
30+
) {
31+
val scope = rememberCoroutineScope()
32+
val animatableOffset = remember { Animatable(300f) }
33+
var offsetY by remember { mutableFloatStateOf(0f) }
34+
var isDismissing by remember { mutableStateOf(false) }
35+
36+
// 등장 애니메이션
37+
LaunchedEffect(Unit) {
38+
animatableOffset.animateTo(
39+
targetValue = 0f,
40+
animationSpec = tween(durationMillis = 300)
41+
)
42+
}
43+
44+
// 바깥 클릭 감지
45+
Box(
46+
modifier = Modifier
47+
.fillMaxSize()
48+
.clickable(
49+
indication = null,
50+
interactionSource = remember { MutableInteractionSource() }
51+
) {
52+
if (!isDismissing) {
53+
isDismissing = true
54+
scope.launch {
55+
animatableOffset.animateTo(300f, tween(300))
56+
onDismiss()
57+
}
58+
}
59+
}
60+
.zIndex(1f)
61+
)
62+
63+
// BottomSheet 본체
64+
Box(
65+
modifier = Modifier
66+
.fillMaxSize()
67+
.zIndex(2f),
68+
contentAlignment = Alignment.BottomCenter
69+
) {
70+
Box(
71+
modifier = Modifier
72+
.fillMaxWidth()
73+
.offset(y = (offsetY + animatableOffset.value).dp)
74+
.background(
75+
color = colors.DarkGrey,
76+
shape = RoundedCornerShape(topEnd = 12.dp, topStart = 12.dp)
77+
)
78+
.pointerInput(Unit) {
79+
detectVerticalDragGestures(
80+
onVerticalDrag = { _, dragAmount ->
81+
if (dragAmount > 0) {
82+
offsetY += dragAmount / 2
83+
}
84+
},
85+
onDragEnd = {
86+
if (offsetY > 100f && !isDismissing) {
87+
isDismissing = true
88+
scope.launch {
89+
animatableOffset.animateTo(300f, tween(300))
90+
onDismiss()
91+
}
92+
} else {
93+
offsetY = 0f
94+
}
95+
}
96+
)
97+
}
98+
.clickable(enabled = true) {}
99+
) {
100+
Column(modifier = Modifier.fillMaxWidth()) {
101+
content()
102+
}
103+
}
104+
}
105+
}
106+
107+
108+
@Preview()
109+
@Composable
110+
fun PreviewCustomBottomSheet() {
111+
var showSheet by remember { mutableStateOf(true) }
112+
113+
ThipTheme {
114+
Box(Modifier.fillMaxSize()) {
115+
Text(
116+
text = "Main Content Area",
117+
color = Color.White,
118+
modifier = Modifier
119+
.align(Alignment.Center)
120+
)
121+
122+
if (showSheet) {
123+
CustomBottomSheet(
124+
onDismiss = { showSheet = false }
125+
) {
126+
Text(
127+
"바텀 시트 예시",
128+
color = Color.White,
129+
modifier = Modifier.padding(bottom = 16.dp)
130+
)
131+
Button(
132+
onClick = { showSheet = false },
133+
modifier = Modifier.fillMaxWidth()
134+
) {
135+
Text("닫기", color = Color.Black)
136+
}
137+
}
138+
}
139+
}
140+
}
141+
}

0 commit comments

Comments
 (0)