-
Notifications
You must be signed in to change notification settings - Fork 3
[UI] 공통 알림 페이지 구현 완료 #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
85135e6
d24d2ec
387ed2e
5c618a0
87036dd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| package com.texthip.thip.ui.common.alarmpage.component | ||
|
|
||
| import androidx.compose.foundation.layout.Arrangement | ||
| import androidx.compose.foundation.layout.Row | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.getValue | ||
| import androidx.compose.runtime.mutableStateOf | ||
| import androidx.compose.runtime.remember | ||
| import androidx.compose.runtime.setValue | ||
| import androidx.compose.ui.res.stringResource | ||
| import androidx.compose.ui.tooling.preview.Preview | ||
| import androidx.compose.ui.unit.dp | ||
| import com.texthip.thip.R | ||
| import com.texthip.thip.ui.common.buttons.OptionChipButton | ||
|
|
||
| @Composable | ||
| fun AlarmFilterRow( | ||
| selectedStates: BooleanArray, | ||
| onToggle: (Int) -> Unit | ||
| ) { | ||
| Row( | ||
| horizontalArrangement = Arrangement.spacedBy(12.dp) | ||
| ) { | ||
| OptionChipButton( | ||
| text = stringResource(R.string.alarm_feed), | ||
| isFilled = true, | ||
| onClick = { onToggle(0) } | ||
| ) | ||
| OptionChipButton( | ||
| text = stringResource(R.string.alarm_group), | ||
| isFilled = true, | ||
| onClick = { onToggle(1) } | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| @Preview(showBackground = true) | ||
| @Composable | ||
| fun AlarmFilterRowPreview() { | ||
| var selectedStates by remember { mutableStateOf(booleanArrayOf(false, false)) } | ||
|
|
||
| AlarmFilterRow( | ||
| selectedStates = selectedStates, | ||
| onToggle = { idx -> | ||
| selectedStates = selectedStates.copyOf().also { it[idx] = !it[idx] } | ||
| } | ||
| ) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.texthip.thip.ui.common.alarmpage.mock | ||
|
|
||
| data class AlarmItem( | ||
| val id: Int, | ||
| val badgeText: String, | ||
| val title: String, | ||
| val message: String, | ||
| val timeAgo: String, | ||
| var isRead: Boolean | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| package com.texthip.thip.ui.common.alarmpage.screen | ||
|
|
||
| import androidx.compose.foundation.background | ||
| import androidx.compose.foundation.layout.Arrangement | ||
| import androidx.compose.foundation.layout.Column | ||
| import androidx.compose.foundation.layout.PaddingValues | ||
| import androidx.compose.foundation.layout.Spacer | ||
| import androidx.compose.foundation.layout.fillMaxSize | ||
| import androidx.compose.foundation.layout.height | ||
| import androidx.compose.foundation.layout.padding | ||
| import androidx.compose.foundation.lazy.LazyColumn | ||
| import androidx.compose.foundation.lazy.items | ||
| import androidx.compose.material3.Icon | ||
| import androidx.compose.material3.Text | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.getValue | ||
| 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.res.painterResource | ||
| import androidx.compose.ui.res.stringResource | ||
| import androidx.compose.ui.tooling.preview.Preview | ||
| import androidx.compose.ui.unit.dp | ||
| import com.texthip.thip.R | ||
| import com.texthip.thip.ui.common.alarmpage.component.AlarmFilterRow | ||
| import com.texthip.thip.ui.common.alarmpage.mock.AlarmItem | ||
| import com.texthip.thip.ui.common.cards.CardAlarm | ||
| import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar | ||
| import com.texthip.thip.ui.theme.ThipTheme | ||
| import com.texthip.thip.ui.theme.ThipTheme.colors | ||
| import com.texthip.thip.ui.theme.ThipTheme.typography | ||
|
|
||
| @Composable | ||
| fun AlarmScreen( | ||
| alarmItems: List<AlarmItem>, onCardClick: (AlarmItem) -> Unit = {} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 사용되지 않는 매개변수 처리
onClick = {
+ onCardClick(alarm)
alarms = alarms.map {
if (it.id == alarm.id) it.copy(isRead = true) else it
}
}또는 이 매개변수가 필요하지 않다면 제거하는 것을 고려해보세요.
🤖 Prompt for AI Agents |
||
| ) { | ||
| var selectedStates by remember { mutableStateOf(booleanArrayOf(false, false)) } | ||
| var alarms by remember { mutableStateOf(alarmItems) } | ||
|
Comment on lines
+36
to
+40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 상태 관리 개선 필요
- var alarms by remember { mutableStateOf(alarmItems) }
+ var alarms by rememberSaveable { mutableStateOf(alarmItems) }또는 ViewModel 패턴을 사용하여 상태를 관리하는 것이 더 나은 접근 방법입니다. 🤖 Prompt for AI Agents |
||
|
|
||
| val filteredList = when { | ||
| selectedStates[0] && !selectedStates[1] -> alarms.filter { it.badgeText == stringResource(R.string.alarm_feed) } | ||
| !selectedStates[0] && selectedStates[1] -> alarms.filter { it.badgeText == stringResource(R.string.alarm_group) } | ||
| else -> alarms | ||
| } | ||
|
|
||
| Column( | ||
| Modifier | ||
| .fillMaxSize() | ||
| ) { | ||
| DefaultTopAppBar( | ||
| title = stringResource(R.string.alarm_string), | ||
| onLeftClick = {}, | ||
| ) | ||
| Column( | ||
| Modifier | ||
| .fillMaxSize() | ||
| .padding(horizontal = 20.dp) | ||
| ) { | ||
| Spacer(modifier = Modifier.height(20.dp)) | ||
| AlarmFilterRow( | ||
| selectedStates = selectedStates, onToggle = { idx -> | ||
| selectedStates = selectedStates.copyOf().also { it[idx] = !it[idx] } | ||
| }) | ||
| Spacer(modifier = Modifier.height(20.dp)) | ||
|
|
||
| if (filteredList.isEmpty()) { | ||
|
|
||
| Column( | ||
| modifier = Modifier.fillMaxSize(), | ||
| verticalArrangement = Arrangement.Center, | ||
| horizontalAlignment = Alignment.CenterHorizontally | ||
| ) { | ||
| Icon( | ||
| painter = painterResource(R.drawable.ic_notification), | ||
| contentDescription = null, | ||
| tint = colors.Grey02, | ||
| ) | ||
| Spacer(modifier = Modifier.height(12.dp)) | ||
| Text( | ||
| text = stringResource(R.string.alarm_notification_comment), | ||
| style = typography.smalltitle_sb600_s16_h20, | ||
| color = colors.Grey01 | ||
| ) | ||
| } | ||
| } else { | ||
| LazyColumn( | ||
| verticalArrangement = Arrangement.spacedBy(20.dp), | ||
| contentPadding = PaddingValues(bottom = 20.dp), | ||
| modifier = Modifier.fillMaxSize() | ||
| ) { | ||
| items(filteredList, key = { it.id }) { alarm -> | ||
| CardAlarm( | ||
| badgeText = alarm.badgeText, | ||
| title = alarm.title, | ||
| message = alarm.message, | ||
| timeAgo = alarm.timeAgo, | ||
| isRead = alarm.isRead, | ||
| onClick = { | ||
| alarms = alarms.map { | ||
| if (it.id == alarm.id) it.copy(isRead = true) else it | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| @Preview(showBackground = true) | ||
| @Composable | ||
| fun AlarmScreenPreview() { | ||
| ThipTheme { | ||
| AlarmScreen( | ||
| alarmItems = listOf( | ||
| AlarmItem(1, "피드", "내 글을 좋아합니다.", "user123님이 내 글에 좋아요를 눌렀어요.", "2", false), | ||
| AlarmItem(2, "모임", "같이 읽기를 시작했어요!", "모임방에서 20분 동안 같이 읽기가 시작되었어요!", "7", false), | ||
| AlarmItem(3, "피드", "내 글에 댓글이 달렸어요.", "user1: 진짜 공감합니다!", "2025.01.12", true), | ||
| AlarmItem(4, "모임", "투표가 시작되었어요!", "투표지를 먼저 열람합니다.", "17", false) | ||
| ) | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| @Preview(showBackground = true) | ||
| @Composable | ||
| fun AlarmScreenEmptyPreview() { | ||
| ThipTheme { | ||
| AlarmScreen( | ||
| alarmItems = emptyList() | ||
| ) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,8 @@ import androidx.compose.foundation.layout.Column | |
| import androidx.compose.foundation.layout.Row | ||
| import androidx.compose.foundation.layout.Spacer | ||
| import androidx.compose.foundation.layout.fillMaxWidth | ||
| import androidx.compose.foundation.layout.height | ||
| import androidx.compose.foundation.layout.offset | ||
| import androidx.compose.foundation.layout.padding | ||
| import androidx.compose.foundation.layout.size | ||
| import androidx.compose.foundation.layout.width | ||
|
|
@@ -42,8 +44,8 @@ fun CardAlarm( | |
| message: String, | ||
| timeAgo: String, | ||
| isRead: Boolean = false, | ||
| containerColorUnread: Color = colors.DarkGrey, // 안읽음 상태의 배경색 | ||
| containerColorRead: Color = colors.DarkGrey02, // 읽음 상태의 배경색 | ||
| containerColorUnread: Color = colors.DarkGrey02, // 안읽음 상태의 배경색 | ||
| containerColorRead: Color = colors.DarkGrey03, // 읽음 상태의 배경색 | ||
| onClick: () -> Unit = {} | ||
| ) { | ||
| Card( | ||
|
|
@@ -86,19 +88,20 @@ fun CardAlarm( | |
| ) | ||
| } | ||
|
|
||
| Spacer(modifier = Modifier.width(12.dp)) | ||
| Spacer(modifier = Modifier.width(8.dp)) | ||
|
|
||
| // 내용 (제목, 빨간 점, 시간, 메시지) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 스크린샷에서 빨간점의 위치가 피그마랑 살짝 다른거같은데 .. 제 눈이 이상한걸수도 있고 .. ㅎ.ㅎ... 작아서 잘 모르겠긴 한데 .. 아무튼 그렇습니다 수정할 수 있으면 수정해주세욧
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이전에 했던거라 생각 못했는데 확인하고 수정하겠습니다! |
||
| Column( | ||
| modifier = Modifier.weight(1f) | ||
| modifier = Modifier.weight(1f), | ||
| verticalArrangement = Arrangement.Center | ||
| ) { | ||
| Row( | ||
| modifier = Modifier.fillMaxWidth(), | ||
| verticalAlignment = Alignment.CenterVertically | ||
| ) { | ||
| Text( | ||
| text = title, | ||
| style = typography.menu_sb600_s14_h24, | ||
| style = typography.view_m500_s14, | ||
| color = if (isRead) colors.Grey01 else colors.White, | ||
| modifier = Modifier.weight(1f), | ||
| maxLines = 1, | ||
|
|
@@ -110,7 +113,6 @@ fun CardAlarm( | |
| ) { | ||
| // 안읽음 상태일 때만 빨간 점 | ||
| if (!isRead) { | ||
| Spacer(modifier = Modifier.width(8.dp)) | ||
| Box( | ||
| modifier = Modifier | ||
| .size(6.dp) | ||
|
|
@@ -121,15 +123,18 @@ fun CardAlarm( | |
|
|
||
| Text( | ||
| text = timeAgo + stringResource(R.string.time_ago), | ||
| style = typography.view_m500_s12_h20, | ||
| style = typography.timedate_r400_s11, | ||
| color = if (isRead) colors.Grey02 else colors.Grey01, | ||
| modifier = Modifier | ||
| .padding(top = 7.dp, end = 2.dp) | ||
| .offset(y = (-5).dp) | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Spacer(modifier = Modifier.width(12.dp)) | ||
| Spacer(modifier = Modifier.height(10.dp)) | ||
|
|
||
| Text( | ||
| text = message, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
필터 상태가 UI에 반영되지 않는 문제가 있습니다.
selectedStates매개변수를 받고 있지만 실제로는 사용하지 않고 있습니다. 모든 버튼이 항상isFilled=true로 설정되어 있어 필터의 선택 상태가 시각적으로 표현되지 않습니다.다음과 같이 수정하여 선택 상태를 반영하도록 해주세요:
OptionChipButton( text = stringResource(R.string.alarm_feed), - isFilled = true, + isFilled = selectedStates[0], onClick = { onToggle(0) } ) OptionChipButton( text = stringResource(R.string.alarm_group), - isFilled = true, + isFilled = selectedStates[1], onClick = { onToggle(1) } )📝 Committable suggestion
🤖 Prompt for AI Agents