-
Notifications
You must be signed in to change notification settings - Fork 1
통계 상세 화면 기본 세팅 #97
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
통계 상세 화면 기본 세팅 #97
Changes from all commits
0ceaf0e
4a2eb33
e92030b
03d9562
3b80b22
7b63a92
a022b51
b3f458b
fb0ef73
f66610c
b72168d
e147fd2
436412c
122ddc4
bc00104
0ae1783
dbac3fe
5a19e99
c8c24f8
0f83d9f
8f4cc2a
6602c24
a2969d1
7775d18
6972cf1
29dd0eb
a4e1685
7d82d7e
904b2e9
2fb9ad6
7ec42dd
f2d4819
a06a313
09e8b7b
6c575d3
b569b14
f1db709
a80ac45
f165054
3caa0e6
cfd1d33
336c69f
235443b
f8bab45
cb4471d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| package com.twix.designsystem.components.stats | ||
|
|
||
| import androidx.compose.foundation.background | ||
| import androidx.compose.foundation.border | ||
| import androidx.compose.foundation.layout.Box | ||
| import androidx.compose.foundation.layout.aspectRatio | ||
| import androidx.compose.foundation.layout.fillMaxSize | ||
| import androidx.compose.foundation.layout.size | ||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.remember | ||
| import androidx.compose.ui.Alignment | ||
| import androidx.compose.ui.Modifier | ||
| import androidx.compose.ui.draw.clip | ||
| import androidx.compose.ui.draw.rotate | ||
| import androidx.compose.ui.layout.ContentScale | ||
| import androidx.compose.ui.platform.LocalContext | ||
| import androidx.compose.ui.text.style.TextAlign | ||
| import androidx.compose.ui.unit.dp | ||
| import coil3.compose.AsyncImage | ||
| import coil3.request.ImageRequest | ||
| import coil3.request.crossfade | ||
| import com.twix.designsystem.components.text.AppText | ||
| import com.twix.designsystem.theme.CommonColor | ||
| import com.twix.designsystem.theme.DimmedColor | ||
| import com.twix.designsystem.theme.GrayColor | ||
| import com.twix.domain.model.enums.AppTextStyle | ||
| import com.twix.domain.model.stats.detail.CompletedDate | ||
| import com.twix.ui.extension.noRippleClickable | ||
| import java.time.LocalDate | ||
|
|
||
| @Composable | ||
| fun PictureDayCell( | ||
| date: LocalDate, | ||
| completed: CompletedDate?, | ||
| onDateSelected: (LocalDate) -> Unit, | ||
| modifier: Modifier = Modifier, | ||
| ) { | ||
chanho0908 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| val showBackgroundCard = completed?.myImageUrl != null && completed.partnerImageUrl != null | ||
chanho0908 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| val hasImage = completed?.partnerImageUrl != null || completed?.myImageUrl != null | ||
|
|
||
| val textColor = if (hasImage) CommonColor.White else GrayColor.C500 | ||
| val borderColor = if (showBackgroundCard) CommonColor.White else GrayColor.C400 | ||
| val cornerShape = RoundedCornerShape(7.dp) | ||
| val context = LocalContext.current | ||
|
|
||
| Box( | ||
| modifier = | ||
| modifier | ||
| .aspectRatio(1f) | ||
| .noRippleClickable { onDateSelected(date) }, | ||
chanho0908 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| contentAlignment = Alignment.Center, | ||
| ) { | ||
| if (showBackgroundCard) { | ||
| Box( | ||
| modifier = | ||
| Modifier | ||
| .size(36.dp) | ||
| .rotate(-16f) | ||
| .border(1.dp, GrayColor.C400, cornerShape) | ||
| .background(CommonColor.White, cornerShape), | ||
| ) | ||
| } | ||
|
|
||
| Box( | ||
| modifier = | ||
| Modifier | ||
| .size(36.dp) | ||
| .clip(cornerShape) | ||
| .then( | ||
| if (showBackgroundCard) { | ||
| Modifier.border(1.dp, borderColor, cornerShape) | ||
| } else { | ||
| Modifier | ||
| }, | ||
| ), | ||
| contentAlignment = Alignment.Center, | ||
| ) { | ||
| val displayImageUrl = completed?.partnerImageUrl ?: completed?.myImageUrl | ||
| if (displayImageUrl != null) { | ||
| val imageRequest = | ||
| remember(displayImageUrl, context) { | ||
| ImageRequest | ||
| .Builder(context) | ||
| .data(displayImageUrl) | ||
| .crossfade(true) | ||
| .build() | ||
| } | ||
|
|
||
| AsyncImage( | ||
| model = imageRequest, | ||
| contentDescription = null, | ||
| contentScale = ContentScale.Crop, | ||
| modifier = Modifier.fillMaxSize(), | ||
| ) | ||
|
|
||
| Box( | ||
| modifier = | ||
| Modifier | ||
| .fillMaxSize() | ||
| .background(DimmedColor.D020), | ||
| ) | ||
| } | ||
|
|
||
| AppText( | ||
| text = date.dayOfMonth.toString(), | ||
| style = AppTextStyle.B1, | ||
| color = textColor, | ||
| textAlign = TextAlign.Center, | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| package com.twix.designsystem.components.stats | ||
|
|
||
| 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.Spacer | ||
| import androidx.compose.foundation.layout.fillMaxWidth | ||
| import androidx.compose.foundation.layout.height | ||
| import androidx.compose.foundation.layout.size | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.ui.Modifier | ||
| import androidx.compose.ui.res.stringResource | ||
| import androidx.compose.ui.text.style.TextAlign | ||
| import androidx.compose.ui.tooling.preview.Preview | ||
| import androidx.compose.ui.unit.dp | ||
| import com.twix.designsystem.R | ||
| import com.twix.designsystem.components.stats.model.StatsCalendarUiModel | ||
| import com.twix.designsystem.components.text.AppText | ||
| import com.twix.designsystem.theme.GrayColor | ||
| import com.twix.designsystem.theme.TwixTheme | ||
| import com.twix.domain.model.enums.AppTextStyle | ||
| import com.twix.domain.model.stats.detail.CompletedDate | ||
| import java.time.LocalDate | ||
|
|
||
| @Composable | ||
| fun StatsCalendar( | ||
| uiModel: StatsCalendarUiModel, | ||
| onSelectedDate: (LocalDate) -> Unit, | ||
| modifier: Modifier = Modifier, | ||
| ) { | ||
| Column(modifier = modifier.fillMaxWidth()) { | ||
| DayOfWeekHeader() | ||
|
|
||
| Spacer(modifier = Modifier.height(16.dp)) | ||
|
|
||
| Column( | ||
| modifier = Modifier.fillMaxWidth(), | ||
| verticalArrangement = Arrangement.spacedBy(20.dp), | ||
| ) { | ||
| uiModel.weeks.forEach { week -> | ||
| Row( | ||
| modifier = Modifier.fillMaxWidth(), | ||
| ) { | ||
| week.forEach { date -> | ||
| if (date == null) { | ||
| Box(modifier = Modifier.weight(1f)) | ||
| } else { | ||
| PictureDayCell( | ||
| date = date, | ||
| completed = uiModel.completedDateMap[date], | ||
| onDateSelected = onSelectedDate, | ||
| modifier = Modifier.weight(1f), | ||
|
Comment on lines
+49
to
+53
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. 날짜 셀의 접근성 맥락(스크린리더 읽기 정보)이 부족합니다. 현재는 클릭 가능 셀이지만 TalkBack/VoiceOver에서 “날짜 + 완료 상태”를 충분히 전달하기 어렵습니다. 왜 문제가 되냐면, 숫자만 읽히면 사용자가 어떤 날짜를 선택하는지 맥락 파악이 어렵기 때문입니다. As per coding guidelines, 🤖 Prompt for AI Agents |
||
| ) | ||
| } | ||
| } | ||
|
|
||
| if (week.size < 7) { | ||
| repeat(7 - week.size) { | ||
| Box(modifier = Modifier.weight(1f)) | ||
| } | ||
| } | ||
| } | ||
chanho0908 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Composable | ||
| private fun DayOfWeekHeader() { | ||
| val days = | ||
| listOf( | ||
| stringResource(R.string.word_sunday), | ||
| stringResource(R.string.word_monday), | ||
| stringResource(R.string.word_tuesday), | ||
| stringResource(R.string.word_wednesday), | ||
| stringResource(R.string.word_thursday), | ||
| stringResource(R.string.word_friday), | ||
| stringResource(R.string.word_saturday), | ||
| ) | ||
| Row( | ||
| modifier = Modifier.fillMaxWidth(), | ||
| ) { | ||
| days.forEach { day -> | ||
| AppText( | ||
| text = day, | ||
| style = AppTextStyle.B2, | ||
| color = GrayColor.C300, | ||
| modifier = | ||
| Modifier | ||
| .weight(1f) | ||
| .height(24.dp), | ||
| textAlign = TextAlign.Center, | ||
| ) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Preview(showBackground = true) | ||
| @Composable | ||
| private fun StatsCalendarPreview() { | ||
| TwixTheme { | ||
| StatsCalendar( | ||
| uiModel = | ||
| StatsCalendarUiModel.create( | ||
| currentDate = LocalDate.of(2026, 2, 1), | ||
| completedDate = | ||
| listOf( | ||
| CompletedDate( | ||
| LocalDate.of(2026, 2, 1), | ||
| "https://picsum.photos/100", | ||
| "https://picsum.photos/100", | ||
| ), | ||
| CompletedDate( | ||
| LocalDate.of(2026, 2, 3), | ||
| "https://picsum.photos/101", | ||
| null, | ||
| ), | ||
| ), | ||
| ), | ||
| onSelectedDate = { }, | ||
| ) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package com.twix.designsystem.components.stats.model | ||
|
|
||
| import androidx.compose.runtime.Immutable | ||
| import com.twix.domain.model.stats.detail.CompletedDate | ||
| import java.time.LocalDate | ||
|
|
||
| @Immutable | ||
| data class StatsCalendarUiModel( | ||
| val currentDate: LocalDate = LocalDate.now(), | ||
| val completedDateMap: Map<LocalDate, CompletedDate> = emptyMap(), | ||
| val weeks: List<List<LocalDate?>> = emptyList(), | ||
| ) { | ||
| companion object { | ||
| private const val WEEK_LENGTH = 7 | ||
|
|
||
| fun create( | ||
| currentDate: LocalDate, | ||
| completedDate: List<CompletedDate>, | ||
| ): StatsCalendarUiModel { | ||
| val firstDayOfMonth = currentDate.withDayOfMonth(1) | ||
| val lastDay = currentDate.lengthOfMonth() | ||
| val emptyCellsBefore = firstDayOfMonth.dayOfWeek.value % WEEK_LENGTH | ||
|
|
||
| val calendarItems = mutableListOf<LocalDate?>() | ||
| repeat(emptyCellsBefore) { calendarItems.add(null) } | ||
| for (i in 1..lastDay) { | ||
| calendarItems.add(currentDate.withDayOfMonth(i)) | ||
| } | ||
|
|
||
| return StatsCalendarUiModel( | ||
| currentDate = currentDate, | ||
| completedDateMap = completedDate.associateBy { it.date }, | ||
| weeks = calendarItems.chunked(WEEK_LENGTH), | ||
| ) | ||
| } | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.