Skip to content

Commit

Permalink
AttendanceCodeDialog 구현 (#852)
Browse files Browse the repository at this point in the history
* Attendance screen compose setting (#676)

* feat: implement NewAttendanceActivity

* feat: implement NewAttendanceViewModel

* chore: add compose-lifecycle dependency

* feat: define AttendanceAction

* feat: implement screens

* feat: use SoptTheme in designsystem

* chore: data class -> class로 변경

* chore: 람다 프로퍼티 이름 명시

* chore: SoptTheme darkTheme 기본값 사용

* chore: 필요없는 함수 제거

* chore: 구현 안 된 함수에 TODO 삽입

* chore: 동작하지 않는 Preview 제거

* chore: AttendanceAction 내 뷰모델 참조 제거

* chore: code format 변경

* chore: make stamp design system internal

* chore: extract string resource

* feat: implement AttendanceCodeCard

* feat: implement AttendanceCodeCardList

* chore: change logic

* feat: implement AttendanceCodeDialog

* feat: implement attendance button

* chore: string resource 추출

* chore: change parameter List to ImmutableList

* chore: reformat codee

# Conflicts:
#	core/designsystem/src/main/java/org/sopt/official/designsystem/Color.kt
  • Loading branch information
giovannijunseokim committed Jan 4, 2025
1 parent 0dd4036 commit 04d414d
Show file tree
Hide file tree
Showing 9 changed files with 286 additions and 8 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ dependencies {
implementation(libs.coil.core)
implementation(libs.profileinstaller)
implementation(libs.firebase.messaging.lifecycle.ktx)
implementation(libs.kotlin.collections.immutable)
}

secrets {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ class NewAttendanceViewModel @Inject constructor(
fetchData()
}

private val _uiState: MutableStateFlow<AttendanceUiState> =
MutableStateFlow(AttendanceUiState.Loading)
private val _uiState: MutableStateFlow<AttendanceUiState> = MutableStateFlow(AttendanceUiState.Loading)
val uiState: StateFlow<AttendanceUiState> = _uiState

private var fakeTitle: String = ""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package org.sopt.official.feature.attendance.compose

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
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.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import org.sopt.official.R
import org.sopt.official.designsystem.Black40
import org.sopt.official.designsystem.Gray60
import org.sopt.official.designsystem.SoptTheme
import org.sopt.official.feature.attendance.compose.component.AttendanceCodeCardList
import org.sopt.official.feature.attendance.model.AttendanceType

@Composable
fun AttendanceCodeDialog(
codes: ImmutableList<String>,
inputCodes: ImmutableList<String?>,
attendanceType: AttendanceType,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
) {
Dialog(onDismissRequest = onDismissRequest) {
Column(
modifier
.background(
color = SoptTheme.colors.onSurface700,
shape = RoundedCornerShape(size = 10.dp)
)
.padding(all = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
painter = painterResource(id = R.drawable.ic_close),
contentDescription = stringResource(id = R.string.close),
tint = SoptTheme.colors.onSurface10,
modifier = Modifier
.align(Alignment.End)
.clickable(onClick = onDismissRequest)
)
Text(
text = stringResource(R.string.attendance_do, attendanceType.type),
style = SoptTheme.typography.heading18B,
color = SoptTheme.colors.onSurface10
)
Spacer(modifier = Modifier.height(10.dp))
Text(
text = stringResource(R.string.attendance_code_description),
style = SoptTheme.typography.body13M,
color = SoptTheme.colors.onSurface300
)
Spacer(modifier = Modifier.height(24.dp))
AttendanceCodeCardList(
codes = inputCodes,
onTextChange = {},
onTextFieldFull = {},
)
if (codes != inputCodes) {
Spacer(modifier = Modifier.height(24.dp))
Text(
text = stringResource(R.string.attendance_code_does_not_match),
style = SoptTheme.typography.label12SB,
color = SoptTheme.colors.error
)
}
Spacer(modifier = Modifier.height(32.dp))
Button(
onClick = { /*TODO*/ },
modifier = Modifier
.fillMaxWidth(),
shape = RoundedCornerShape(size = 6.dp),
colors = ButtonColors(
containerColor = SoptTheme.colors.onSurface10,
contentColor = SoptTheme.colors.onSurface950,
disabledContainerColor = Black40,
disabledContentColor = Gray60,
),
enabled = codes == inputCodes
) {
Text(
text = stringResource(R.string.attendance_dialog_button),
style = SoptTheme.typography.body13M,
)
}
}
}
}

@Preview
@Composable
private fun AttendanceCodeDialogPreview(
@PreviewParameter(AttendanceCodeDialogPreviewParameterProvider::class) parameter: AttendanceCodeDialogPreviewParameter,
) {
SoptTheme {
AttendanceCodeDialog(
codes = parameter.codes,
inputCodes = parameter.inputCodes,
attendanceType = parameter.attendanceType,
modifier = Modifier.fillMaxWidth(),
onDismissRequest = {}
)
}
}

data class AttendanceCodeDialogPreviewParameter(
val codes: ImmutableList<String>,
val inputCodes: ImmutableList<String?>,
val attendanceType: AttendanceType,
)

class AttendanceCodeDialogPreviewParameterProvider :
PreviewParameterProvider<AttendanceCodeDialogPreviewParameter> {
override val values: Sequence<AttendanceCodeDialogPreviewParameter> =
sequenceOf(
AttendanceCodeDialogPreviewParameter(
codes = persistentListOf("1", "2", "3", "4", "5"),
inputCodes = persistentListOf("1", "2", "3", null, null),
AttendanceType.FIRST,
),
AttendanceCodeDialogPreviewParameter(
codes = persistentListOf("1", "2", "3", "4", "5"),
inputCodes = persistentListOf("1", "2", "3", "4", "5"),
AttendanceType.FIRST,
),
AttendanceCodeDialogPreviewParameter(
codes = persistentListOf("1", "2", "3", "4", "5"),
inputCodes = persistentListOf("1", "2", "3", null, null),
AttendanceType.SECOND,
),
AttendanceCodeDialogPreviewParameter(
codes = persistentListOf("1", "2", "3", "4", "5"),
inputCodes = persistentListOf("1", "2", "3", "4", "5"),
AttendanceType.SECOND,
),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ package org.sopt.official.feature.attendance.compose
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
Expand All @@ -15,6 +18,7 @@ import org.sopt.official.feature.attendance.compose.component.AttendanceTopAppBa
import org.sopt.official.feature.attendance.model.AttendanceAction
import org.sopt.official.feature.attendance.model.AttendanceUiState

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AttendanceRoute(onClickBackIcon: () -> Unit) {
val viewModel: NewAttendanceViewModel = viewModel()
Expand All @@ -27,7 +31,7 @@ fun AttendanceRoute(onClickBackIcon: () -> Unit) {
onClickBackIcon = onClickBackIcon,
onClickRefreshIcon = viewModel::fetchData,
)
},
}
) { innerPaddingValues ->
Column(
modifier = Modifier
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.sopt.official.feature.attendance.compose.component

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import org.sopt.official.designsystem.SoptTheme

@Composable
fun AttendanceCodeCard(
text: String,
onTextChange: (String) -> Unit,
onTextFieldFull: () -> Unit,
modifier: Modifier = Modifier,
textMaxLength: Int = 1,
) {
BasicTextField(
value = text,
onValueChange = { newText: String ->
if (newText.length < textMaxLength) {
onTextChange(newText)
} else {
onTextFieldFull()
}
},
modifier = modifier
.background(
color = if (text.isEmpty()) SoptTheme.colors.onSurface600
else SoptTheme.colors.onSurface800,
shape = RoundedCornerShape(8.dp)
)
.border(
width = 1.dp,
color = if (text.isEmpty()) SoptTheme.colors.onSurface500
else SoptTheme.colors.primary,
shape = RoundedCornerShape(8.dp)
)
.padding(horizontal = 17.dp, vertical = 18.dp)
.width(10.dp),
textStyle = SoptTheme.typography.heading16B.copy(color = SoptTheme.colors.primary)
)
}

@Preview
@Composable
private fun AttendanceCodeCardPreview(
@PreviewParameter(AttendanceCodeCardPreviewParameterProvider::class) text: String,
) {
SoptTheme {
AttendanceCodeCard(
text = text,
onTextChange = {},
onTextFieldFull = {}
)
}
}

private class AttendanceCodeCardPreviewParameterProvider : PreviewParameterProvider<String> {
override val values: Sequence<String> = sequenceOf("", "8")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.sopt.official.feature.attendance.compose.component

import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.sopt.official.designsystem.SoptTheme

@Composable
fun AttendanceCodeCardList(
codes: List<String?>,
onTextChange: (newText: String) -> Unit,
onTextFieldFull: () -> Unit,
modifier: Modifier = Modifier,
) {
Row(modifier = modifier) {
repeat(codes.size) { index ->
AttendanceCodeCard(
text = codes[index] ?: "",
onTextChange = onTextChange,
onTextFieldFull = onTextFieldFull
)
if (index < codes.size) {
Spacer(modifier = Modifier.width(width = 12.dp))
}
}
}
}

@Preview
@Composable
private fun AttendanceCodeCardListPreview() {
SoptTheme {
AttendanceCodeCardList(
codes = listOf("8", "8", "8", null, null),
onTextChange = {},
onTextFieldFull = {})
}
}
4 changes: 4 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<string name="main_content_header">SOPT를 알차게 즐기고 싶다면?</string>
<string name="soptamp">SOPT-AMP!</string>
<string name="poke">콕 찌르기</string>
<string name="close">닫기</string>

<!-- 메인홈 -->
<string name="main_title_non_member">안녕하세요,\nSOPT의 열정이 되어주세요!</string>
Expand Down Expand Up @@ -102,6 +103,9 @@
<string name="go_back">뒤로 가기</string>
<string name="refresh">새로고침</string>
<string name="attendance_app_bar_title">출석 조회하기</string>
<string name="attendance_do">%1$s하기</string>
<string name="attendance_code_description">출석 코드 다섯 자리를 입력해주세요.</string>
<string name="attendance_code_does_not_match">코드가 일치하지 않아요!</string>

<!-- 알림 -->
<string name="toolbar_notification">알림</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ val White = Color(0xFFFFFFFF)
val Black = Color(0xFF000000)
val Black80 = Color(0xFF1C1D1E)
val Black60 = Color(0xFF2C2D2E)
val Black40 = Color(0xFF3C3D40)
val Gray950 = Color(0xFF0F1012)
val Gray900 = Color(0xFF17181C)
val Gray800 = Color(0xFF202025)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class SoptColors(
onSurface20: Color,
onSurface10: Color,
onSurface5: Color,
isLight: Boolean
isLight: Boolean,
) {
var white by mutableStateOf(white)
private set
Expand Down Expand Up @@ -217,7 +217,7 @@ fun soptLightColors(
onSurface30: Color = Gray300,
onSurface20: Color = Gray200,
onSurface10: Color = Gray100,
onSurface5: Color = Gray50
onSurface5: Color = Gray50,
) = SoptColors(
white,
black,
Expand Down Expand Up @@ -278,7 +278,7 @@ fun soptDarkColors(
onSurface30: Color = Gray300,
onSurface20: Color = Gray200,
onSurface10: Color = Gray100,
onSurface5: Color = Gray50
onSurface5: Color = Gray50,
) = SoptColors(
white,
black,
Expand Down Expand Up @@ -325,14 +325,18 @@ private val LocalSoptTypography = staticCompositionLocalOf<SoptTypography> {
* Color에 접근하고 싶을때 SoptTheme.colors.primary 이런식으로 접근하면 됩니다.
* Typo를 변경하고 싶다면 SoptTheme.typography.h1 이런식으로 접근하면 됩니다.
* */
object SoptTheme {
internal object SoptTheme {
val colors: SoptColors @Composable get() = LocalSoptColors.current

val typography: SoptTypography @Composable get() = LocalSoptTypography.current
}

@Composable
fun ProvideSoptColorsAndTypography(colors: SoptColors, typography: SoptTypography, content: @Composable () -> Unit) {
fun ProvideSoptColorsAndTypography(
colors: SoptColors,
typography: SoptTypography,
content: @Composable () -> Unit,
) {
val provideColors = remember { colors.copy() }
provideColors.update(colors)
val provideTypography = remember { typography.copy() }
Expand Down

0 comments on commit 04d414d

Please sign in to comment.