From 5358615f4168b9930b990a9a45e9a77863de5bcc Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Wed, 28 Aug 2024 14:46:08 +0900 Subject: [PATCH 01/22] =?UTF-8?q?[refactor]=20Res=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EA=B0=81=20feature=20=EB=AA=A8=EB=93=88=20=EB=B3=84=EB=A1=9C?= =?UTF-8?q?=20=EA=B4=80=EB=A6=AC=ED=95=98=EB=8A=94=20=EB=B0=A9=EC=8B=9D?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20refactoring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 다국어 지원을 하는 경우가 아니라면, 개발의 편의성 증진을 위해 각 모듈에서 사용하는 res 파일은 해당 모듈이 가지고 있는게 합리적 DroidKnights, nia 도 같은 방식을 따르고 있음 --- .../core/designsystem/component/Dialog.kt | 19 +---- .../src/main/res/values/strings.xml | 66 +++++------------- .../com/tripmate/android/mate/MateScreen.kt | 2 +- .../android/mate/viewmodel/MateUiState.kt | 12 ++-- .../personalization/Question1Screen.kt | 1 - .../personalization/Question2Screen.kt | 1 - .../personalization/Question3Screen.kt | 1 - .../personalization/Question4Screen.kt | 1 - .../feature/personalization/ResultScreen.kt | 1 - .../personalization/TripStyleScreen.kt | 1 - .../feature/personalization/UserInfoScreen.kt | 13 ++-- .../component/BirthDateTextField.kt | 45 ------------ .../component/GenderSelectionBox.kt | 2 +- .../component/UnderAgeDialog.kt | 2 +- .../viewmodel/PersonalizationUiState.kt | 2 +- .../viewmodel/PersonalizationViewModel.kt | 2 +- .../src/main/res/drawable/img_backpack.png | Bin .../res/drawable/img_camera_with_flash.png | Bin .../src/main/res/drawable/img_compass.png | Bin .../main/res/drawable/img_deciduous_tree.png | Bin .../src/main/res/drawable/img_diving_mask.png | Bin .../src/main/res/drawable/img_female.png | Bin .../main/res/drawable/img_ferris_wheel.png | Bin .../main/res/drawable/img_framed_picture.png | Bin .../src/main/res/drawable/img_male.png | Bin .../main/res/drawable/img_mobile_phone.png | Bin .../src/main/res/drawable/img_pot_of_food.png | Bin .../main/res/drawable/img_running_shoe.png | Bin .../main/res/drawable/img_shopping_bags.png | Bin .../res/drawable/img_statue_of_liberty.png | Bin .../src/main/res/values/strings.xml | 45 ++++++++++++ 31 files changed, 82 insertions(+), 134 deletions(-) delete mode 100644 feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/BirthDateTextField.kt rename {core/designsystem => feature/personalization}/src/main/res/drawable/img_backpack.png (100%) rename {core/designsystem => feature/personalization}/src/main/res/drawable/img_camera_with_flash.png (100%) rename {core/designsystem => feature/personalization}/src/main/res/drawable/img_compass.png (100%) rename {core/designsystem => feature/personalization}/src/main/res/drawable/img_deciduous_tree.png (100%) rename {core/designsystem => feature/personalization}/src/main/res/drawable/img_diving_mask.png (100%) rename {core/designsystem => feature/personalization}/src/main/res/drawable/img_female.png (100%) rename {core/designsystem => feature/personalization}/src/main/res/drawable/img_ferris_wheel.png (100%) rename {core/designsystem => feature/personalization}/src/main/res/drawable/img_framed_picture.png (100%) rename {core/designsystem => feature/personalization}/src/main/res/drawable/img_male.png (100%) rename {core/designsystem => feature/personalization}/src/main/res/drawable/img_mobile_phone.png (100%) rename {core/designsystem => feature/personalization}/src/main/res/drawable/img_pot_of_food.png (100%) rename {core/designsystem => feature/personalization}/src/main/res/drawable/img_running_shoe.png (100%) rename {core/designsystem => feature/personalization}/src/main/res/drawable/img_shopping_bags.png (100%) rename {core/designsystem => feature/personalization}/src/main/res/drawable/img_statue_of_liberty.png (100%) create mode 100644 feature/personalization/src/main/res/values/strings.xml diff --git a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/Dialog.kt b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/Dialog.kt index 8f2f513b..1968c6f1 100644 --- a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/Dialog.kt +++ b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/Dialog.kt @@ -143,6 +143,7 @@ fun ServerErrorDialog( cancelTextResId = null, onCancelClick = {}, onConfirmClick = onRetryClick, + modifier = Modifier.padding(16.dp), ) } } @@ -162,24 +163,6 @@ fun NetworkErrorDialog( cancelTextResId = null, onCancelClick = {}, onConfirmClick = onRetryClick, - ) - } -} - -@ComponentPreview -@Composable -fun TripMateDialogPreview() { - TripmateTheme { - TripmateDialog( - onDismissRequest = {}, - titleResId = R.string.under_age_title, - iconResId = null, - iconDescription = "", - descriptionResId = R.string.under_age_description, - cancelTextResId = null, - confirmTextResId = R.string.under_age_confirm, - onCancelClick = {}, - onConfirmClick = {}, modifier = Modifier.padding(16.dp), ) } diff --git a/core/designsystem/src/main/res/values/strings.xml b/core/designsystem/src/main/res/values/strings.xml index c7a35a38..c3e4ce81 100644 --- a/core/designsystem/src/main/res/values/strings.xml +++ b/core/designsystem/src/main/res/values/strings.xml @@ -10,49 +10,24 @@ 네트워크 문제 와이파이와 데이터 접속을 확인해주세요 - - 숙소 주최 파티에서 장기자랑하면\n술값이 무료! 일 때 당신의 선택은? - 선택하기 - 술값이 무료라고? 당연 참가!😄 - 그냥 내 돈 내고 말지... - 설레는 여향길, 기차를 타고 가는데\n나른함에 잠이 쏟아진다.\n이때 드는 생각은? - 잠든 사이에 내 짐 훔쳐가면 어떡해? 잡을 수 있을까? - 아무 걱정하지 않고 냅다 잠이 든다 - \'나 우울해서 떡볶이 시켜먹었어\nㅜㅜ\'친구가 문자를 보냈을 때\n당신의 답장은? - 무슨 일 있었어? 나한테 고민 이야기 해봐 다 들어줄게! - 떡볶이를 먹었어? 너 다이어트 한다며. 떡볶이가 다이어트할 때 최악의 음식이래... - 강원도 여행을 가기로 했다.\n여행이 일주일 남았다면? - 여행 계획을 시간단위, 분 단위로 촘촘히 세운다 - 첫째날 갈 곳, 둘째날 갈 곳 등 장소정도 찾아본다 - 여행 전 날 어디 갈지 생각해보기로 한다 - 당신의 여행 스타일을\n알려주세요. - 최대 3개 - 키워드는 최대 3개까지 선택가능합니다. - 쇼핑매니아\n기념품은 필수 - 풍경보며\n걷는게 좋아 - 랜드마크는\n무조건 - 여행은\n액티비티 - 인스타에\n추억기록 - 여행은\n모험이지 - 자유로운\n배낭여행 - 인생샷 남기러\n여행가자! - 문화체험은\n여행의 이유 - 맛집 방문은\n필수코스 - 편안한게 최고\n여행은 힐링 - 테마파크는\n필수 - 당신에 대해 더 알려주세요. - 추천과 동행글에만 이용돼요.\n걱정하지 마세요🤗 - 성별을 골라주세요 - 여자 - 남자 - 생년월일을 입력해주세요 - YYMMDD - 올바른 생년월일을 입력해주세요. - 만 19세 미만 미성년자는\n회원가입이 불가능해요. - 다음에 다시 만나요.🥲 - 다음에 올게요 - 트립메이트 시작하기 + + 카카오 로그인 + + 알림 + 아직 알림이 없습니다. + 동행 모집 제목 + ex)서퍼비치에서 점심 같이드실분 + 여행 장소 + ex)양양 서퍼비치 + 일정 + 동행 유형 + 작성 완료 + 모든 항목을 필수로 입력해주세요😉 + 성별, 연령대 설정 + 오픈 카톡방 링크 + + 전체 동행 모집 중 음식 @@ -63,11 +38,4 @@ 동행 모집만 보기 동행 모집 - - 카카오 로그인 - - - 알림 - 아직 알림이 없습니다. - diff --git a/feature/mate/src/main/kotlin/com/tripmate/android/mate/MateScreen.kt b/feature/mate/src/main/kotlin/com/tripmate/android/mate/MateScreen.kt index f07f83bb..ecac9476 100644 --- a/feature/mate/src/main/kotlin/com/tripmate/android/mate/MateScreen.kt +++ b/feature/mate/src/main/kotlin/com/tripmate/android/mate/MateScreen.kt @@ -670,7 +670,7 @@ fun GetPoiCardViewPreview() { title = "Title 1", address = "강원도 춘천시", description = "This is the description for item 1", - imageRes = R.drawable.img_camera_with_flash, + imageRes = R.drawable.ic_mypage, lat = 37.5, lon = 127.0, ), diff --git a/feature/mate/src/main/kotlin/com/tripmate/android/mate/viewmodel/MateUiState.kt b/feature/mate/src/main/kotlin/com/tripmate/android/mate/viewmodel/MateUiState.kt index 4ecd95a1..bcf2d8df 100644 --- a/feature/mate/src/main/kotlin/com/tripmate/android/mate/viewmodel/MateUiState.kt +++ b/feature/mate/src/main/kotlin/com/tripmate/android/mate/viewmodel/MateUiState.kt @@ -17,7 +17,7 @@ data class MateUiState( "Searching Title 1", "강원도 춘천시", "This is the description for item 1", - R.drawable.img_camera_with_flash, + R.drawable.ic_mypage, lat = 37.517235, lon = 127.047325, isSearching = true, @@ -26,7 +26,7 @@ data class MateUiState( "Searching Title 2", "강원도 춘천시", "This is the description for item 2", - R.drawable.img_camera_with_flash, + R.drawable.ic_mypage, lat = 37.5, lon = 127.2, ), @@ -34,7 +34,7 @@ data class MateUiState( "Searching Title 3", "강원도 춘천시", "This is the description for item 3", - R.drawable.img_camera_with_flash, + R.drawable.ic_mypage, lat = 37.5, lon = 127.3, isSearching = true, @@ -48,7 +48,7 @@ data class MateUiState( "Title 1", "강원도 춘천시", "This is the description for item 1", - R.drawable.img_camera_with_flash, + R.drawable.ic_mypage, lat = 37.5, lon = 127.0, isSearching = true, @@ -57,7 +57,7 @@ data class MateUiState( "Title 2", "강원도 춘천시", "This is the description for item 2", - R.drawable.img_camera_with_flash, + R.drawable.ic_mypage, lat = 37.6, lon = 127.0, ), @@ -65,7 +65,7 @@ data class MateUiState( "Title 3", "강원도 춘천시", "This is the description for item 3", - R.drawable.img_camera_with_flash, + R.drawable.ic_mypage, lat = 37.7, lon = 127.0, isSearching = true, diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question1Screen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question1Screen.kt index 32830767..deb2c874 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question1Screen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question1Screen.kt @@ -14,7 +14,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.tripmate.android.core.designsystem.R import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tripmate.android.core.common.ObserveAsEvents import com.tripmate.android.core.designsystem.component.TripmateButton diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question2Screen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question2Screen.kt index d18670c6..5780b98d 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question2Screen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question2Screen.kt @@ -15,7 +15,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tripmate.android.core.common.ObserveAsEvents -import com.tripmate.android.core.designsystem.R import com.tripmate.android.core.designsystem.component.TripmateButton import com.tripmate.android.core.designsystem.theme.Medium16_SemiBold import com.tripmate.android.core.designsystem.theme.TripmateTheme diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question3Screen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question3Screen.kt index d29d1ab9..40cc07e3 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question3Screen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question3Screen.kt @@ -15,7 +15,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tripmate.android.core.common.ObserveAsEvents -import com.tripmate.android.core.designsystem.R import com.tripmate.android.core.designsystem.component.TripmateButton import com.tripmate.android.core.designsystem.theme.Medium16_SemiBold import com.tripmate.android.core.designsystem.theme.TripmateTheme diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question4Screen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question4Screen.kt index a22db6d4..e47ec31c 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question4Screen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question4Screen.kt @@ -15,7 +15,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tripmate.android.core.common.ObserveAsEvents -import com.tripmate.android.core.designsystem.R import com.tripmate.android.core.designsystem.component.TripmateButton import com.tripmate.android.core.designsystem.theme.Medium16_SemiBold import com.tripmate.android.core.designsystem.theme.TripmateTheme diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/ResultScreen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/ResultScreen.kt index f294d388..7df02498 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/ResultScreen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/ResultScreen.kt @@ -19,7 +19,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tripmate.android.core.common.ObserveAsEvents -import com.tripmate.android.core.designsystem.R import com.tripmate.android.core.designsystem.component.TripmateButton import com.tripmate.android.core.designsystem.theme.Gray001 import com.tripmate.android.core.designsystem.theme.Large20_Bold diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/TripStyleScreen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/TripStyleScreen.kt index 3ba9ec64..c26ba933 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/TripStyleScreen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/TripStyleScreen.kt @@ -34,7 +34,6 @@ import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tripmate.android.core.common.ObserveAsEvents -import com.tripmate.android.core.designsystem.R import com.tripmate.android.core.designsystem.component.TripmateButton import com.tripmate.android.core.designsystem.theme.Background02 import com.tripmate.android.core.designsystem.theme.Background03 diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt index 7c6549ab..8aefaee7 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt @@ -19,8 +19,8 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tripmate.android.core.common.ObserveAsEvents -import com.tripmate.android.core.designsystem.R import com.tripmate.android.core.designsystem.component.TripmateButton +import com.tripmate.android.core.designsystem.component.TripmateTextField import com.tripmate.android.core.designsystem.theme.Gray001 import com.tripmate.android.core.designsystem.theme.Gray004 import com.tripmate.android.core.designsystem.theme.Large20_Bold @@ -28,7 +28,6 @@ import com.tripmate.android.core.designsystem.theme.Medium16_SemiBold import com.tripmate.android.core.designsystem.theme.Small14_Reg import com.tripmate.android.core.designsystem.theme.TripmateTheme import com.tripmate.android.core.ui.DevicePreview -import com.tripmate.android.feature.personalization.component.BirthRateTextField import com.tripmate.android.feature.personalization.component.GenderSelectionBox import com.tripmate.android.feature.personalization.component.UnderAgeDialog import com.tripmate.android.feature.personalization.viewmodel.Gender @@ -142,10 +141,14 @@ fun UserInfoContent( color = Gray001, ) Spacer(modifier = Modifier.height(8.dp)) - BirthRateTextField( - birthDateText = uiState.birthDate, - updateBirthDateText = { text -> onAction(PersonalizationUiAction.OnBirthDateUpdated(text)) }, + TripmateTextField( + text = uiState.birthDate, + onTextChange = { text -> onAction(PersonalizationUiAction.OnBirthDateUpdated(text)) }, + searchTextHintRes = R.string.birth_date_hint, clearText = { onAction(PersonalizationUiAction.OnClearIconClicked) }, + modifier = Modifier + .fillMaxWidth() + .height(52.dp), errorText = uiState.birthDateErrorText?.asString(context), ) } diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/BirthDateTextField.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/BirthDateTextField.kt deleted file mode 100644 index 2471f1f6..00000000 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/BirthDateTextField.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.tripmate.android.feature.personalization.component - -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.tripmate.android.core.designsystem.ComponentPreview -import com.tripmate.android.core.designsystem.R -import com.tripmate.android.core.designsystem.component.TripmateTextField -import com.tripmate.android.core.designsystem.theme.TripmateTheme - -@Composable -fun BirthRateTextField( - birthDateText: String, - updateBirthDateText: (String) -> Unit, - clearText: () -> Unit, - modifier: Modifier = Modifier, - errorText: String? = null, -) { - TripmateTextField( - text = birthDateText, - onTextChange = updateBirthDateText, - searchTextHintRes = R.string.birth_date_hint, - clearText = clearText, - modifier = modifier, - errorText = errorText, - maxLength = null, - ) -} - -@ComponentPreview -@Composable -fun BirthRateTextFieldPreview() { - TripmateTheme { - BirthRateTextField( - birthDateText = "", - updateBirthDateText = {}, - modifier = Modifier - .fillMaxWidth() - .height(52.dp), - clearText = {}, - ) - } -} diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/GenderSelectionBox.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/GenderSelectionBox.kt index e0b5cd37..64fcf38f 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/GenderSelectionBox.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/GenderSelectionBox.kt @@ -24,7 +24,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.tripmate.android.core.designsystem.ComponentPreview -import com.tripmate.android.core.designsystem.R import com.tripmate.android.core.designsystem.theme.Background02 import com.tripmate.android.core.designsystem.theme.Background03 import com.tripmate.android.core.designsystem.theme.Gray001 @@ -32,6 +31,7 @@ import com.tripmate.android.core.designsystem.theme.Medium16_Mid import com.tripmate.android.core.designsystem.theme.Medium16_SemiBold import com.tripmate.android.core.designsystem.theme.Primary03 import com.tripmate.android.core.designsystem.theme.TripmateTheme +import com.tripmate.android.feature.personalization.R import com.tripmate.android.feature.personalization.viewmodel.Gender @Composable diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/UnderAgeDialog.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/UnderAgeDialog.kt index ee046cbe..97fca756 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/UnderAgeDialog.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/UnderAgeDialog.kt @@ -2,9 +2,9 @@ package com.tripmate.android.feature.personalization.component import androidx.compose.runtime.Composable import com.tripmate.android.core.designsystem.ComponentPreview -import com.tripmate.android.core.designsystem.R import com.tripmate.android.core.designsystem.component.TripmateDialog import com.tripmate.android.core.designsystem.theme.TripmateTheme +import com.tripmate.android.feature.personalization.R @Composable fun UnderAgeDialog( diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/viewmodel/PersonalizationUiState.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/viewmodel/PersonalizationUiState.kt index 77c7f92f..47abd69c 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/viewmodel/PersonalizationUiState.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/viewmodel/PersonalizationUiState.kt @@ -2,7 +2,7 @@ package com.tripmate.android.feature.personalization.viewmodel import com.tripmate.android.core.common.UiText import com.tripmate.android.domain.entity.TripStyleEntity -import com.tripmate.android.core.designsystem.R +import com.tripmate.android.feature.personalization.R import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/viewmodel/PersonalizationViewModel.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/viewmodel/PersonalizationViewModel.kt index 740d4b13..8493891f 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/viewmodel/PersonalizationViewModel.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/viewmodel/PersonalizationViewModel.kt @@ -3,9 +3,9 @@ package com.tripmate.android.feature.personalization.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.tripmate.android.core.common.UiText -import com.tripmate.android.core.designsystem.R import com.tripmate.android.domain.entity.TripStyleEntity import com.tripmate.android.domain.repository.PersonalizationRepository +import com.tripmate.android.feature.personalization.R import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow diff --git a/core/designsystem/src/main/res/drawable/img_backpack.png b/feature/personalization/src/main/res/drawable/img_backpack.png similarity index 100% rename from core/designsystem/src/main/res/drawable/img_backpack.png rename to feature/personalization/src/main/res/drawable/img_backpack.png diff --git a/core/designsystem/src/main/res/drawable/img_camera_with_flash.png b/feature/personalization/src/main/res/drawable/img_camera_with_flash.png similarity index 100% rename from core/designsystem/src/main/res/drawable/img_camera_with_flash.png rename to feature/personalization/src/main/res/drawable/img_camera_with_flash.png diff --git a/core/designsystem/src/main/res/drawable/img_compass.png b/feature/personalization/src/main/res/drawable/img_compass.png similarity index 100% rename from core/designsystem/src/main/res/drawable/img_compass.png rename to feature/personalization/src/main/res/drawable/img_compass.png diff --git a/core/designsystem/src/main/res/drawable/img_deciduous_tree.png b/feature/personalization/src/main/res/drawable/img_deciduous_tree.png similarity index 100% rename from core/designsystem/src/main/res/drawable/img_deciduous_tree.png rename to feature/personalization/src/main/res/drawable/img_deciduous_tree.png diff --git a/core/designsystem/src/main/res/drawable/img_diving_mask.png b/feature/personalization/src/main/res/drawable/img_diving_mask.png similarity index 100% rename from core/designsystem/src/main/res/drawable/img_diving_mask.png rename to feature/personalization/src/main/res/drawable/img_diving_mask.png diff --git a/core/designsystem/src/main/res/drawable/img_female.png b/feature/personalization/src/main/res/drawable/img_female.png similarity index 100% rename from core/designsystem/src/main/res/drawable/img_female.png rename to feature/personalization/src/main/res/drawable/img_female.png diff --git a/core/designsystem/src/main/res/drawable/img_ferris_wheel.png b/feature/personalization/src/main/res/drawable/img_ferris_wheel.png similarity index 100% rename from core/designsystem/src/main/res/drawable/img_ferris_wheel.png rename to feature/personalization/src/main/res/drawable/img_ferris_wheel.png diff --git a/core/designsystem/src/main/res/drawable/img_framed_picture.png b/feature/personalization/src/main/res/drawable/img_framed_picture.png similarity index 100% rename from core/designsystem/src/main/res/drawable/img_framed_picture.png rename to feature/personalization/src/main/res/drawable/img_framed_picture.png diff --git a/core/designsystem/src/main/res/drawable/img_male.png b/feature/personalization/src/main/res/drawable/img_male.png similarity index 100% rename from core/designsystem/src/main/res/drawable/img_male.png rename to feature/personalization/src/main/res/drawable/img_male.png diff --git a/core/designsystem/src/main/res/drawable/img_mobile_phone.png b/feature/personalization/src/main/res/drawable/img_mobile_phone.png similarity index 100% rename from core/designsystem/src/main/res/drawable/img_mobile_phone.png rename to feature/personalization/src/main/res/drawable/img_mobile_phone.png diff --git a/core/designsystem/src/main/res/drawable/img_pot_of_food.png b/feature/personalization/src/main/res/drawable/img_pot_of_food.png similarity index 100% rename from core/designsystem/src/main/res/drawable/img_pot_of_food.png rename to feature/personalization/src/main/res/drawable/img_pot_of_food.png diff --git a/core/designsystem/src/main/res/drawable/img_running_shoe.png b/feature/personalization/src/main/res/drawable/img_running_shoe.png similarity index 100% rename from core/designsystem/src/main/res/drawable/img_running_shoe.png rename to feature/personalization/src/main/res/drawable/img_running_shoe.png diff --git a/core/designsystem/src/main/res/drawable/img_shopping_bags.png b/feature/personalization/src/main/res/drawable/img_shopping_bags.png similarity index 100% rename from core/designsystem/src/main/res/drawable/img_shopping_bags.png rename to feature/personalization/src/main/res/drawable/img_shopping_bags.png diff --git a/core/designsystem/src/main/res/drawable/img_statue_of_liberty.png b/feature/personalization/src/main/res/drawable/img_statue_of_liberty.png similarity index 100% rename from core/designsystem/src/main/res/drawable/img_statue_of_liberty.png rename to feature/personalization/src/main/res/drawable/img_statue_of_liberty.png diff --git a/feature/personalization/src/main/res/values/strings.xml b/feature/personalization/src/main/res/values/strings.xml new file mode 100644 index 00000000..864fa9cf --- /dev/null +++ b/feature/personalization/src/main/res/values/strings.xml @@ -0,0 +1,45 @@ + + + 숙소 주최 파티에서 장기자랑하면\n술값이 무료! 일 때 당신의 선택은? + 선택하기 + 술값이 무료라고? 당연 참가!😄 + 그냥 내 돈 내고 말지... + 설레는 여향길, 기차를 타고 가는데\n나른함에 잠이 쏟아진다.\n이때 드는 생각은? + 잠든 사이에 내 짐 훔쳐가면 어떡해? 잡을 수 있을까? + 아무 걱정하지 않고 냅다 잠이 든다 + \'나 우울해서 떡볶이 시켜먹었어\nㅜㅜ\'친구가 문자를 보냈을 때\n당신의 답장은? + 무슨 일 있었어? 나한테 고민 이야기 해봐 다 들어줄게! + 떡볶이를 먹었어? 너 다이어트 한다며. 떡볶이가 다이어트할 때 최악의 음식이래... + 강원도 여행을 가기로 했다.\n여행이 일주일 남았다면? + 여행 계획을 시간단위, 분 단위로 촘촘히 세운다 + 첫째날 갈 곳, 둘째날 갈 곳 등 장소정도 찾아본다 + 여행 전 날 어디 갈지 생각해보기로 한다 + 당신의 여행 스타일을\n알려주세요. + 최대 3개 + 키워드는 최대 3개까지 선택가능합니다. + 쇼핑매니아\n기념품은 필수 + 풍경보며\n걷는게 좋아 + 랜드마크는\n무조건 + 여행은\n액티비티 + 인스타에\n추억기록 + 여행은\n모험이지 + 자유로운\n배낭여행 + 인생샷 남기러\n여행가자! + 문화체험은\n여행의 이유 + 맛집 방문은\n필수코스 + 편안한게 최고\n여행은 힐링 + 테마파크는\n필수 + 당신에 대해 더 알려주세요. + 추천과 동행글에만 이용돼요.\n걱정하지 마세요🤗 + 성별을 골라주세요 + 여자 + 남자 + 생년월일을 입력해주세요 + YYMMDD + 올바른 생년월일을 입력해주세요. + 만 19세 미만 미성년자는\n회원가입이 불가능해요. + 다음에 다시 만나요.🥲 + 다음에 올게요 + 트립메이트 시작하기 + + From a38e563040e6c6ca57955605ff9f0baf71a55c9b Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Wed, 28 Aug 2024 14:46:32 +0900 Subject: [PATCH 02/22] =?UTF-8?q?[feat]=20=EB=8F=99=ED=96=89=20=EB=AA=A8?= =?UTF-8?q?=EC=A7=91=20=EB=AA=A8=EB=93=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/main/build.gradle.kts | 1 + feature/mate-recruit/.gitignore | 1 + feature/mate-recruit/build.gradle.kts | 26 +++ .../feature/recruit/MateRecruitScreen.kt | 205 ++++++++++++++++++ .../navigation/MateRecruitNavigation.kt | 25 +++ .../recruit/viewmodel/MateRecruitUiAction.kt | 27 +++ .../recruit/viewmodel/MateRecruitUiEvent.kt | 7 + .../recruit/viewmodel/MateRecruitUiState.kt | 19 ++ .../recruit/viewmodel/MateRecruitViewModel.kt | 127 +++++++++++ settings.gradle.kts | 1 + 10 files changed, 439 insertions(+) create mode 100644 feature/mate-recruit/.gitignore create mode 100644 feature/mate-recruit/build.gradle.kts create mode 100644 feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt create mode 100644 feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/navigation/MateRecruitNavigation.kt create mode 100644 feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt create mode 100644 feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiEvent.kt create mode 100644 feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt create mode 100644 feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt diff --git a/feature/main/build.gradle.kts b/feature/main/build.gradle.kts index 1982d52c..ee0b536c 100644 --- a/feature/main/build.gradle.kts +++ b/feature/main/build.gradle.kts @@ -16,6 +16,7 @@ dependencies { projects.feature.mate, projects.feature.writing, projects.feature.notification, + projects.feature.mateRecruit, projects.feature.map, libs.androidx.activity.compose, diff --git a/feature/mate-recruit/.gitignore b/feature/mate-recruit/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/mate-recruit/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/mate-recruit/build.gradle.kts b/feature/mate-recruit/build.gradle.kts new file mode 100644 index 00000000..fd4222dd --- /dev/null +++ b/feature/mate-recruit/build.gradle.kts @@ -0,0 +1,26 @@ +@file:Suppress("INLINE_FROM_HIGHER_PLATFORM") + +plugins { + alias(libs.plugins.tripmate.android.feature) +} + +android { + namespace = "com.tripmate.android.feature.mate_recruit" + + buildFeatures { + buildConfig = true + } +} + +dependencies { + implementations( + projects.feature.navigator, + + libs.kotlinx.datetime, + libs.kotlinx.collections.immutable, + libs.androidx.activity.compose, + libs.androidx.splash, + libs.compose.system.ui.controller, + libs.timber, + ) +} diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt new file mode 100644 index 00000000..ddb8095e --- /dev/null +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt @@ -0,0 +1,205 @@ +package com.tripmate.android.feature.recruit + +import androidx.compose.foundation.layout.Box +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.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.tripmate.android.core.common.ObserveAsEvents +import com.tripmate.android.core.designsystem.R +import com.tripmate.android.core.designsystem.component.TripmateButton +import com.tripmate.android.core.designsystem.component.TripmateTextField +import com.tripmate.android.core.designsystem.theme.Gray001 +import com.tripmate.android.core.designsystem.theme.Gray004 +import com.tripmate.android.core.designsystem.theme.Large20_Bold +import com.tripmate.android.core.designsystem.theme.Medium16_SemiBold +import com.tripmate.android.core.designsystem.theme.Small14_Reg +import com.tripmate.android.core.designsystem.theme.TripmateTheme +import com.tripmate.android.core.ui.DevicePreview +import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiAction +import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiState +import com.tripmate.android.feature.recruit.viewmodel.MateRecruitViewModel + +@Composable +fun MateRecruitRoute( + innerPadding: PaddingValues, + popBackStack: () -> Unit, + viewModel: MateRecruitViewModel = hiltViewModel(), +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + LocalContext.current + + ObserveAsEvents(flow = viewModel.uiEvent) { event -> + when (event) { + else -> popBackStack() + } + } + + MateRecruitScreen( + uiState = uiState, + innerPadding = innerPadding, + onAction = viewModel::onAction, + ) +} + +@Composable +fun MateRecruitScreen( + uiState: MateRecruitUiState, + innerPadding: PaddingValues, + onAction: (MateRecruitUiAction) -> Unit, +) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), + ) { + UserInfoContent( + uiState = uiState, + onAction = onAction, + ) + } +} + +@Composable +fun UserInfoContent( + uiState: MateRecruitUiState, + onAction: (MateRecruitUiAction) -> Unit, + modifier: Modifier = Modifier, +) { + val context = LocalContext.current + + Box { + Column( + modifier = modifier + .fillMaxSize() + .padding(horizontal = 16.dp), + ) { + Spacer(modifier = Modifier.height(60.dp)) + Text( + text = stringResource(id = R.string.mate_recruit_title), + style = Large20_Bold, + color = Gray001, + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = stringResource(id = R.string.mate_recruit_description), + style = Small14_Reg, + color = Gray004, + textAlign = TextAlign.Center, + ) + Spacer(modifier = Modifier.height(32.dp)) + Text( + text = stringResource(id = R.string.mate_recruit_title), + style = Medium16_SemiBold, + color = Gray001, + ) + Spacer(modifier = Modifier.height(8.dp)) + TripmateTextField( + text = uiState.birthDate, + onTextChange = { text -> onAction(MateRecruitUiAction.OnBirthDateUpdated(text)) }, + searchTextHintRes = R.string.mate_recruit_title_hint, + clearText = { onAction(MateRecruitUiAction.OnClearIconClicked) }, + modifier = Modifier + .fillMaxWidth() + .height(52.dp), + errorText = uiState.birthDateErrorText?.asString(context), + ) + Text( + text = stringResource(id = R.string.trip_location), + style = Medium16_SemiBold, + color = Gray001, + ) + Spacer(modifier = Modifier.height(8.dp)) + TripmateTextField( + text = uiState.birthDate, + onTextChange = { text -> onAction(MateRecruitUiAction.OnBirthDateUpdated(text)) }, + searchTextHintRes = R.string.trip_location_hint, + clearText = { onAction(MateRecruitUiAction.OnClearIconClicked) }, + modifier = Modifier + .fillMaxWidth() + .height(52.dp), + errorText = uiState.birthDateErrorText?.asString(context), + ) + Text( + text = stringResource(id = R.string.schedule), + style = Medium16_SemiBold, + color = Gray001, + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = stringResource(id = R.string.mate_recruit_title), + style = Medium16_SemiBold, + color = Gray001, + ) + Spacer(modifier = Modifier.height(8.dp)) + TripmateTextField( + text = uiState.birthDate, + onTextChange = { text -> onAction(MateRecruitUiAction.OnBirthDateUpdated(text)) }, + searchTextHintRes = R.string.mate_recruit_title_hint, + clearText = { onAction(MateRecruitUiAction.OnClearIconClicked) }, + modifier = Modifier + .fillMaxWidth() + .height(52.dp), + errorText = uiState.birthDateErrorText?.asString(context), + ) + Text( + text = stringResource(id = R.string.mate_type), + style = Medium16_SemiBold, + color = Gray001, + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = stringResource(id = R.string.setting_gender_age), + style = Medium16_SemiBold, + color = Gray001, + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = stringResource(id = R.string.open_kakao_link), + style = Medium16_SemiBold, + color = Gray001, + ) + Spacer(modifier = Modifier.height(8.dp)) + } + TripmateButton( + onClick = {}, + modifier = Modifier + .align(Alignment.BottomCenter) + .fillMaxWidth() + .padding(horizontal = 32.dp, vertical = 20.dp), + contentPadding = PaddingValues(vertical = 17.dp), + enabled = true, + ) { + Text( + text = stringResource(R.string.done), + style = Medium16_SemiBold, + ) + } + } +} + +@DevicePreview +@Composable +fun MateRecruitScreenPreview() { + TripmateTheme { + MateRecruitScreen( + uiState = MateRecruitUiState(), + innerPadding = PaddingValues(), + onAction = {}, + ) + } +} diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/navigation/MateRecruitNavigation.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/navigation/MateRecruitNavigation.kt new file mode 100644 index 00000000..6f978b0c --- /dev/null +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/navigation/MateRecruitNavigation.kt @@ -0,0 +1,25 @@ +package com.tripmate.android.feature.recruit.navigation + +import androidx.compose.foundation.layout.PaddingValues +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import com.tripmate.android.feature.recruit.MateRecruitRoute + +const val MATE_RECRUIT_ROUTE = "mate_recruit_route" + +fun NavController.navigateToMateRecruit() { + navigate(MATE_RECRUIT_ROUTE) +} + +fun NavGraphBuilder.mateRecruitNavGraph( + innerPadding: PaddingValues, + popBackStack: () -> Unit, +) { + composable(route = MATE_RECRUIT_ROUTE) { entry -> + MateRecruitRoute( + innerPadding = innerPadding, + popBackStack = popBackStack, + ) + } +} diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt new file mode 100644 index 00000000..0f2db81d --- /dev/null +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt @@ -0,0 +1,27 @@ +package com.tripmate.android.feature.recruit.viewmodel + +import com.tripmate.android.domain.entity.TripStyleEntity + +sealed interface MateRecruitUiAction { + data class OnTripStyleSelected(val tripStyle: TripStyleEntity) : MateRecruitUiAction + data class OnTripStyleDeselected(val tripStyle: TripStyleEntity) : MateRecruitUiAction + data class OnGenderSelected(val gender: Gender) : MateRecruitUiAction + data class OnBirthDateUpdated(val birthDate: String) : MateRecruitUiAction + data object OnClearIconClicked : MateRecruitUiAction + data class OnSelectClick(val screenType: ScreenType) : MateRecruitUiAction +} + +enum class ScreenType { + QUESTION_1, + QUESTION_2, + QUESTION_3, + QUESTION_4, + TRIP_STYLE, + USER_INFO, + RESULT, +} + +enum class ErrorType { + NETWORK, + SERVER, +} diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiEvent.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiEvent.kt new file mode 100644 index 00000000..6849c7e1 --- /dev/null +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiEvent.kt @@ -0,0 +1,7 @@ +package com.tripmate.android.feature.recruit.viewmodel + +import com.tripmate.android.core.common.UiText + +sealed interface MateRecruitUiEvent { + data class ShowToast(val message: UiText) : MateRecruitUiEvent +} diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt new file mode 100644 index 00000000..13ff5107 --- /dev/null +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt @@ -0,0 +1,19 @@ +package com.tripmate.android.feature.recruit.viewmodel + +import com.tripmate.android.core.common.UiText +import com.tripmate.android.domain.entity.TripStyleEntity +import com.tripmate.android.core.designsystem.R +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.persistentListOf + +data class MateRecruitUiState( + val selectedTripStyles: PersistentList = persistentListOf(), + val selectedGender: Gender = Gender.NOT_SPECIFIED, + val birthDate: String = "", + val birthDateErrorText: UiText? = null, +) + +enum class Gender { + MALE, FEMALE, NOT_SPECIFIED +} diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt new file mode 100644 index 00000000..175d5ff5 --- /dev/null +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt @@ -0,0 +1,127 @@ +package com.tripmate.android.feature.recruit.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.tripmate.android.core.common.UiText +import com.tripmate.android.core.designsystem.R +import com.tripmate.android.domain.entity.TripStyleEntity +import com.tripmate.android.domain.repository.PersonalizationRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import kotlinx.datetime.Clock +import kotlinx.datetime.LocalDate +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime +import javax.inject.Inject + +@HiltViewModel +class MateRecruitViewModel @Inject constructor( + @Suppress("UnusedPrivateProperty") + private val personalizationRepository: PersonalizationRepository, +) : ViewModel() { + private val _uiState = MutableStateFlow(MateRecruitUiState()) + val uiState: StateFlow = _uiState.asStateFlow() + + private val _uiEvent = Channel() + val uiEvent: Flow = _uiEvent.receiveAsFlow() + + enum class BirthDateValidationResult { + VALID, + INVALID_DATE, + UNDERAGE, + } + + fun onAction(action: MateRecruitUiAction) { + when (action) { + is MateRecruitUiAction.OnTripStyleSelected -> addSelectedTripStyle(action.tripStyle) + is MateRecruitUiAction.OnTripStyleDeselected -> removeSelectedTripStyle(action.tripStyle) + is MateRecruitUiAction.OnGenderSelected -> setGender(action.gender) + is MateRecruitUiAction.OnBirthDateUpdated -> setBirthDate(action.birthDate) + is MateRecruitUiAction.OnClearIconClicked -> clearText() + is MateRecruitUiAction.OnSelectClick -> {} + } + } + + private fun addSelectedTripStyle(tripStyle: TripStyleEntity) { + if (_uiState.value.selectedTripStyles.size >= 3) { + viewModelScope.launch {} + return + } + _uiState.update { + it.copy(selectedTripStyles = it.selectedTripStyles.add(tripStyle)) + } + } + + private fun removeSelectedTripStyle(tripStyle: TripStyleEntity) { + _uiState.update { + it.copy(selectedTripStyles = it.selectedTripStyles.remove(tripStyle)) + } + } + + private fun setGender(gender: Gender) { + _uiState.update { it.copy(selectedGender = gender) } + } + + private fun setBirthDate(birthDate: String) { + if (birthDate.length <= 6) { + _uiState.update { it.copy(birthDate = birthDate) } + if (birthDate.length == 6) { + if (validateBirthDate(birthDate) == BirthDateValidationResult.INVALID_DATE) { + _uiState.update { it.copy(birthDateErrorText = null) } + } else { + _uiState.update { it.copy(birthDateErrorText = null) } + } + } + } + } + + private fun clearText() { + _uiState.update { + it.copy( + birthDate = "", + birthDateErrorText = null, + ) + } + } + + @Suppress("SwallowedException") + private fun validateBirthDate(birthDate: String): BirthDateValidationResult { + // 숫자로 만 이루어져 있는지 체크 + if (!birthDate.matches(Regex("[0-9]+"))) return BirthDateValidationResult.INVALID_DATE + + // 유효한 날짜인지 체크 + val year = birthDate.substring(0, 2).toInt() + val month = birthDate.substring(2, 4).toInt() + val day = birthDate.substring(4, 6).toInt() + + if (month < 1 || month > 12 || day < 1 || day > 31) return BirthDateValidationResult.INVALID_DATE + + // 현재 날짜 기준으로 만 19세 이상인지 체크 + val currentYear = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).year % 100 + val fullYear = if (year <= currentYear) 2000 + year else 1900 + year + + try { + val birthDateFormatted = LocalDate(fullYear, month, day) + val today = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date + val age = today.year - birthDateFormatted.year + + return if (age > 19) { + BirthDateValidationResult.VALID + } else if (age == 19) { + val birthDayThisYear = LocalDate(today.year, birthDateFormatted.month, birthDateFormatted.dayOfMonth) + if (birthDayThisYear <= today) BirthDateValidationResult.VALID else BirthDateValidationResult.UNDERAGE + } else { + BirthDateValidationResult.UNDERAGE + } + } catch (e: IllegalArgumentException) { + return BirthDateValidationResult.INVALID_DATE + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index f6c60fe7..0d270d26 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -37,6 +37,7 @@ include( ":feature:login", ":feature:main", ":feature:mypage", + ":feature:mate-recruit", ":feature:navigator", ":feature:splash", ":feature:personalization", From 777a1d9d150822997e28ad95ed128bbfed0c5237 Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Wed, 28 Aug 2024 15:45:59 +0900 Subject: [PATCH 03/22] =?UTF-8?q?[chore]=20font=20includeFontPadding=20?= =?UTF-8?q?=EC=98=B5=EC=85=98=20=EC=84=A4=EC=A0=95=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit default 값이 false 가 되었음... https://developer.android.com/jetpack/androidx/releases/compose-ui#1.6.0-alpha01 --- .../android/core/designsystem/theme/Font.kt | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/theme/Font.kt b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/theme/Font.kt index 70984766..7b4bae03 100644 --- a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/theme/Font.kt +++ b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/theme/Font.kt @@ -23,7 +23,7 @@ val XLarge26_Bold = TextStyle( fontSize = 26.sp, letterSpacing = 0.sp, lineHeight = 33.8.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val XLarge26_Reg = TextStyle( fontFamily = pretendardFamily, @@ -31,7 +31,7 @@ val XLarge26_Reg = TextStyle( fontSize = 26.sp, letterSpacing = 0.sp, lineHeight = 33.8.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val XLarge26_Light = TextStyle( fontFamily = pretendardFamily, @@ -39,7 +39,7 @@ val XLarge26_Light = TextStyle( fontSize = 26.sp, letterSpacing = 0.sp, lineHeight = 33.8.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val Large20_Bold = TextStyle( fontFamily = pretendardFamily, @@ -53,7 +53,7 @@ val Large20_SemiBold = TextStyle( fontSize = 20.sp, letterSpacing = 0.sp, lineHeight = 28.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val Large20_Reg = TextStyle( fontFamily = pretendardFamily, @@ -61,14 +61,14 @@ val Large20_Reg = TextStyle( fontSize = 20.sp, letterSpacing = 0.sp, lineHeight = 28.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val Large20_Light = TextStyle( fontFamily = pretendardFamily, fontWeight = FontWeight.Light, letterSpacing = 0.sp, lineHeight = 28.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val Medium16_SemiBold = TextStyle( fontFamily = pretendardFamily, @@ -76,7 +76,7 @@ val Medium16_SemiBold = TextStyle( fontSize = 16.sp, letterSpacing = 0.sp, lineHeight = 24.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val Medium16_Reg = TextStyle( fontFamily = pretendardFamily, @@ -84,7 +84,7 @@ val Medium16_Reg = TextStyle( fontSize = 16.sp, letterSpacing = 0.sp, lineHeight = 24.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val Medium16_Mid = TextStyle( fontFamily = pretendardFamily, @@ -92,7 +92,7 @@ val Medium16_Mid = TextStyle( fontSize = 16.sp, letterSpacing = 0.sp, lineHeight = 24.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val Medium16_Light = TextStyle( fontFamily = pretendardFamily, @@ -100,7 +100,7 @@ val Medium16_Light = TextStyle( fontSize = 16.sp, letterSpacing = 0.sp, lineHeight = 24.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val Small14_SemiBold = TextStyle( fontFamily = pretendardFamily, @@ -108,7 +108,7 @@ val Small14_SemiBold = TextStyle( fontSize = 14.sp, letterSpacing = 0.sp, lineHeight = 21.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val Small14_Reg = TextStyle( fontFamily = pretendardFamily, @@ -116,7 +116,7 @@ val Small14_Reg = TextStyle( fontSize = 14.sp, letterSpacing = 0.sp, lineHeight = 21.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val Small14_Light = TextStyle( fontFamily = pretendardFamily, @@ -124,7 +124,7 @@ val Small14_Light = TextStyle( fontSize = 14.sp, letterSpacing = 0.sp, lineHeight = 21.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val XSmall12_SemiBold = TextStyle( fontFamily = pretendardFamily, @@ -132,7 +132,7 @@ val XSmall12_SemiBold = TextStyle( fontSize = 12.sp, letterSpacing = 0.sp, lineHeight = 18.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val XSmall12_Reg = TextStyle( fontFamily = pretendardFamily, @@ -140,7 +140,7 @@ val XSmall12_Reg = TextStyle( fontSize = 12.sp, letterSpacing = 0.sp, lineHeight = 18.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val XSmall12_Light = TextStyle( fontFamily = pretendardFamily, @@ -148,7 +148,7 @@ val XSmall12_Light = TextStyle( fontSize = 12.sp, letterSpacing = 0.sp, lineHeight = 18.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) val XSmall12_Mid = TextStyle( fontFamily = pretendardFamily, @@ -156,4 +156,4 @@ val XSmall12_Mid = TextStyle( fontSize = 12.sp, letterSpacing = 0.sp, lineHeight = 18.sp, -).copy(platformStyle = PlatformTextStyle(includeFontPadding = false)) +) From dbaf3d32e832ce837b1ceef5645ce2d3959495a1 Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Thu, 29 Aug 2024 00:37:14 +0900 Subject: [PATCH 04/22] =?UTF-8?q?[feat]=20=ED=99=88=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=20=EB=8F=99=ED=96=89=20=EB=AA=A8=EC=A7=91=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/core/designsystem/theme/Font.kt | 1 - feature/home/build.gradle.kts | 1 + .../android/feature/home/HomeScreen.kt | 27 ++++++++++++++++--- .../feature/home/navigation/HomeNavigation.kt | 2 ++ .../android/feature/main/MainNavController.kt | 5 ++++ .../android/feature/main/MainScreen.kt | 6 +++++ .../navigation/MateRecruitNavigation.kt | 4 +-- 7 files changed, 39 insertions(+), 7 deletions(-) diff --git a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/theme/Font.kt b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/theme/Font.kt index 7b4bae03..53af7017 100644 --- a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/theme/Font.kt +++ b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/theme/Font.kt @@ -1,6 +1,5 @@ package com.tripmate.android.core.designsystem.theme -import androidx.compose.ui.text.PlatformTextStyle import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts index 46bf1339..c889a235 100644 --- a/feature/home/build.gradle.kts +++ b/feature/home/build.gradle.kts @@ -11,6 +11,7 @@ android { dependencies { implementations( projects.core.data, + projects.feature.mateRecruit, libs.kotlinx.collections.immutable, libs.timber, diff --git a/feature/home/src/main/kotlin/com/tripmate/android/feature/home/HomeScreen.kt b/feature/home/src/main/kotlin/com/tripmate/android/feature/home/HomeScreen.kt index 8500c204..ffb43b37 100644 --- a/feature/home/src/main/kotlin/com/tripmate/android/feature/home/HomeScreen.kt +++ b/feature/home/src/main/kotlin/com/tripmate/android/feature/home/HomeScreen.kt @@ -1,37 +1,56 @@ package com.tripmate.android.feature.home import androidx.compose.foundation.layout.Box +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.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.tripmate.android.core.designsystem.component.TripmateButton @Composable internal fun HomeRoute( innerPadding: PaddingValues, + navigateToMateRecruit: () -> Unit, ) { HomeScreen( innerPadding = innerPadding, + navigateToMateRecruit = navigateToMateRecruit, ) } @Composable internal fun HomeScreen( innerPadding: PaddingValues, + navigateToMateRecruit: () -> Unit, ) { Box( modifier = Modifier .fillMaxSize() .padding(innerPadding), ) { - Text( - text = "홈화면", + Column( modifier = Modifier.align(Alignment.Center), - fontSize = 24.sp, - ) + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + text = "홈화면", + fontSize = 24.sp, + ) + Spacer(modifier = Modifier.height(16.dp)) + TripmateButton( + onClick = navigateToMateRecruit, + ) { + Text(text = "동행 모집") + } + } } } diff --git a/feature/home/src/main/kotlin/com/tripmate/android/feature/home/navigation/HomeNavigation.kt b/feature/home/src/main/kotlin/com/tripmate/android/feature/home/navigation/HomeNavigation.kt index 4a2f32e6..854e0ba6 100644 --- a/feature/home/src/main/kotlin/com/tripmate/android/feature/home/navigation/HomeNavigation.kt +++ b/feature/home/src/main/kotlin/com/tripmate/android/feature/home/navigation/HomeNavigation.kt @@ -15,10 +15,12 @@ fun NavController.navigateToHome(navOptions: NavOptions) { fun NavGraphBuilder.homeNavGraph( padding: PaddingValues, + navigateToMateRecruit: () -> Unit, ) { composable(route = HOME_ROUTE) { HomeRoute( innerPadding = padding, + navigateToMateRecruit = navigateToMateRecruit, ) } } diff --git a/feature/main/src/main/kotlin/com/tripmate/android/feature/main/MainNavController.kt b/feature/main/src/main/kotlin/com/tripmate/android/feature/main/MainNavController.kt index 6751ce83..ac7b1fdd 100644 --- a/feature/main/src/main/kotlin/com/tripmate/android/feature/main/MainNavController.kt +++ b/feature/main/src/main/kotlin/com/tripmate/android/feature/main/MainNavController.kt @@ -11,6 +11,7 @@ import androidx.navigation.navOptions import com.tripmate.android.feature.home.navigation.HOME_ROUTE import com.tripmate.android.feature.home.navigation.navigateToHome import com.tripmate.android.feature.mypage.navigation.navigateToMyPage +import com.tripmate.android.feature.recruit.navigation.navigateToMateRecruit import com.tripmate.android.mate.navigation.navigateToMate import com.tripmate.android.notification.navigation.navigateToNotification import com.tripmate.android.writing.navigation.navigateToWriting @@ -47,6 +48,10 @@ internal class MainNavController( } } + fun navigateToMateRecruit() { + navController.navigateToMateRecruit() + } + private fun popBackStack() { navController.popBackStack() } diff --git a/feature/main/src/main/kotlin/com/tripmate/android/feature/main/MainScreen.kt b/feature/main/src/main/kotlin/com/tripmate/android/feature/main/MainScreen.kt index 38900e61..34398c88 100644 --- a/feature/main/src/main/kotlin/com/tripmate/android/feature/main/MainScreen.kt +++ b/feature/main/src/main/kotlin/com/tripmate/android/feature/main/MainScreen.kt @@ -44,6 +44,7 @@ import com.tripmate.android.core.designsystem.theme.TripmateTheme import com.tripmate.android.core.designsystem.theme.XSmall12_Mid import com.tripmate.android.feature.home.navigation.homeNavGraph import com.tripmate.android.feature.mypage.navigation.myPageNavGraph +import com.tripmate.android.feature.recruit.navigation.mateRecruitNavGraph import com.tripmate.android.mate.navigation.mateNavGraph import com.tripmate.android.notification.navigation.notificationNavGraph import com.tripmate.android.writing.navigation.writingNavGraph @@ -76,6 +77,7 @@ internal fun MainScreen( ) { homeNavGraph( padding = innerPadding, + navigateToMateRecruit = navigator::navigateToMateRecruit, ) mateNavGraph( padding = innerPadding, @@ -92,6 +94,10 @@ internal fun MainScreen( padding = innerPadding, // popBackStack = navigator::popBackStackIfNotHome, ) + mateRecruitNavGraph( + padding = innerPadding, + popBackStack = navigator::popBackStackIfNotHome, + ) } } } diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/navigation/MateRecruitNavigation.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/navigation/MateRecruitNavigation.kt index 6f978b0c..556b0a4a 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/navigation/MateRecruitNavigation.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/navigation/MateRecruitNavigation.kt @@ -13,12 +13,12 @@ fun NavController.navigateToMateRecruit() { } fun NavGraphBuilder.mateRecruitNavGraph( - innerPadding: PaddingValues, + padding: PaddingValues, popBackStack: () -> Unit, ) { composable(route = MATE_RECRUIT_ROUTE) { entry -> MateRecruitRoute( - innerPadding = innerPadding, + innerPadding = padding, popBackStack = popBackStack, ) } From 2f05805f044461441f8e15bf135e7c22e6ac0181 Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Thu, 29 Aug 2024 01:27:06 +0900 Subject: [PATCH 05/22] =?UTF-8?q?[feat]=20=EB=8F=99=ED=96=89=20=EB=AA=A8?= =?UTF-8?q?=EC=A7=91=20=ED=99=94=EB=A9=B4=20UI=20=EA=B5=AC=EC=84=B1=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B5=AC=ED=98=84=201=EC=B0=A8=20=EC=99=84?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/core/data/di/RepositoryModule.kt | 6 + .../data/repository/MateRepositoryImpl.kt | 17 ++ .../core/designsystem/component/TextField.kt | 2 +- .../src/main/res/values/strings.xml | 5 + .../domain/entity/GenderAgeGroupEntity.kt | 7 + .../domain/repository/MateRepository.kt | 6 + .../feature/recruit/MateRecruitScreen.kt | 149 +++++++++++------- .../recruit/viewmodel/MateRecruitUiAction.kt | 26 ++- .../recruit/viewmodel/MateRecruitUiState.kt | 20 +-- .../recruit/viewmodel/MateRecruitViewModel.kt | 107 +++---------- 10 files changed, 178 insertions(+), 167 deletions(-) create mode 100644 core/data/src/main/kotlin/com/tripmate/android/core/data/repository/MateRepositoryImpl.kt create mode 100644 core/domain/src/main/kotlin/com/tripmate/android/domain/entity/GenderAgeGroupEntity.kt create mode 100644 core/domain/src/main/kotlin/com/tripmate/android/domain/repository/MateRepository.kt diff --git a/core/data/src/main/kotlin/com/tripmate/android/core/data/di/RepositoryModule.kt b/core/data/src/main/kotlin/com/tripmate/android/core/data/di/RepositoryModule.kt index b20843b2..2d908fde 100644 --- a/core/data/src/main/kotlin/com/tripmate/android/core/data/di/RepositoryModule.kt +++ b/core/data/src/main/kotlin/com/tripmate/android/core/data/di/RepositoryModule.kt @@ -2,10 +2,12 @@ package com.tripmate.android.core.data.di import com.tripmate.android.core.data.repository.NotificationRepositoryImpl import com.tripmate.android.core.data.repository.MapRepositoryImpl +import com.tripmate.android.core.data.repository.MateRepositoryImpl import com.tripmate.android.domain.repository.PersonalizationRepository import com.tripmate.android.core.data.repository.PersonalizationRepositoryImpl import com.tripmate.android.domain.repository.NotificationRepository import com.tripmate.android.domain.repository.MapRepository +import com.tripmate.android.domain.repository.MateRepository import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -27,4 +29,8 @@ internal abstract class RepositoryModule { @Binds @Singleton abstract fun bindNotificationRepository(notificationRepositoryImpl: NotificationRepositoryImpl): NotificationRepository + + @Binds + @Singleton + abstract fun bindMateRepository(mateRepositoryImpl: MateRepositoryImpl): MateRepository } diff --git a/core/data/src/main/kotlin/com/tripmate/android/core/data/repository/MateRepositoryImpl.kt b/core/data/src/main/kotlin/com/tripmate/android/core/data/repository/MateRepositoryImpl.kt new file mode 100644 index 00000000..a1792fb7 --- /dev/null +++ b/core/data/src/main/kotlin/com/tripmate/android/core/data/repository/MateRepositoryImpl.kt @@ -0,0 +1,17 @@ +package com.tripmate.android.core.data.repository + +import com.tripmate.android.core.datastore.PersonalizationDataSource +import com.tripmate.android.domain.repository.MateRepository +import javax.inject.Inject + +internal class MateRepositoryImpl @Inject constructor( + private val personalizationDataSource: PersonalizationDataSource, +) : MateRepository { + override suspend fun checkPersonalizationCompletion(): Boolean { + return personalizationDataSource.checkPersonalizationCompletion() + } + + override suspend fun completePersonalization(flag: Boolean) { + personalizationDataSource.completePersonalization(flag) + } +} diff --git a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt index 25247a64..5789612e 100644 --- a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt +++ b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt @@ -39,8 +39,8 @@ fun TripmateTextField( text: String, onTextChange: (String) -> Unit, @StringRes searchTextHintRes: Int, - clearText: () -> Unit, modifier: Modifier = Modifier, + clearText: () -> Unit = {}, errorText: String? = null, backgroundColor: Color = Color.White, textColor: Color = Gray001, diff --git a/core/designsystem/src/main/res/values/strings.xml b/core/designsystem/src/main/res/values/strings.xml index c3e4ce81..10fe5758 100644 --- a/core/designsystem/src/main/res/values/strings.xml +++ b/core/designsystem/src/main/res/values/strings.xml @@ -37,5 +37,10 @@ 지도 보기 동행 모집만 보기 동행 모집 + 동행 글쓰기 + 동행 모집 내용 + 동행 모집 내용을 작성해주세요. + 오픈 채팅방 개설 후 링크를 입력해주세요 + 오픈 카톡방을 만든 후 작성해주세요 diff --git a/core/domain/src/main/kotlin/com/tripmate/android/domain/entity/GenderAgeGroupEntity.kt b/core/domain/src/main/kotlin/com/tripmate/android/domain/entity/GenderAgeGroupEntity.kt new file mode 100644 index 00000000..76a0edaa --- /dev/null +++ b/core/domain/src/main/kotlin/com/tripmate/android/domain/entity/GenderAgeGroupEntity.kt @@ -0,0 +1,7 @@ +package com.tripmate.android.domain.entity + +data class GenderAgeGroupEntity( + val id: Int = 0, + val textResId: Int = 0, + val isSelected: Boolean = false, +) diff --git a/core/domain/src/main/kotlin/com/tripmate/android/domain/repository/MateRepository.kt b/core/domain/src/main/kotlin/com/tripmate/android/domain/repository/MateRepository.kt new file mode 100644 index 00000000..e68491cb --- /dev/null +++ b/core/domain/src/main/kotlin/com/tripmate/android/domain/repository/MateRepository.kt @@ -0,0 +1,6 @@ +package com.tripmate.android.domain.repository + +interface MateRepository { + suspend fun checkPersonalizationCompletion(): Boolean + suspend fun completePersonalization(flag: Boolean) +} diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt index ddb8095e..9f2c4d5e 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt @@ -1,5 +1,6 @@ package com.tripmate.android.feature.recruit +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -8,27 +9,35 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tripmate.android.core.common.ObserveAsEvents import com.tripmate.android.core.designsystem.R +import com.tripmate.android.core.designsystem.component.TopAppBarNavigationType import com.tripmate.android.core.designsystem.component.TripmateButton import com.tripmate.android.core.designsystem.component.TripmateTextField +import com.tripmate.android.core.designsystem.component.TripmateTopAppBar import com.tripmate.android.core.designsystem.theme.Gray001 import com.tripmate.android.core.designsystem.theme.Gray004 +import com.tripmate.android.core.designsystem.theme.Gray010 import com.tripmate.android.core.designsystem.theme.Large20_Bold import com.tripmate.android.core.designsystem.theme.Medium16_SemiBold -import com.tripmate.android.core.designsystem.theme.Small14_Reg +import com.tripmate.android.core.designsystem.theme.Small14_SemiBold import com.tripmate.android.core.designsystem.theme.TripmateTheme +import com.tripmate.android.core.designsystem.theme.XSmall12_Reg import com.tripmate.android.core.ui.DevicePreview import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiAction import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiState @@ -67,127 +76,157 @@ fun MateRecruitScreen( .fillMaxSize() .padding(innerPadding), ) { - UserInfoContent( - uiState = uiState, - onAction = onAction, - ) + Column { + TripmateTopAppBar( + navigationType = TopAppBarNavigationType.Back, + title = stringResource(id = R.string.mate_writing), + ) + MateRecruitContent( + uiState = uiState, + onAction = onAction, + ) + } } } @Composable -fun UserInfoContent( +fun MateRecruitContent( uiState: MateRecruitUiState, onAction: (MateRecruitUiAction) -> Unit, modifier: Modifier = Modifier, ) { - val context = LocalContext.current - Box { Column( modifier = modifier .fillMaxSize() + .verticalScroll(rememberScrollState()) .padding(horizontal = 16.dp), ) { - Spacer(modifier = Modifier.height(60.dp)) - Text( - text = stringResource(id = R.string.mate_recruit_title), - style = Large20_Bold, - color = Gray001, - ) - Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(16.dp)) Text( text = stringResource(id = R.string.mate_recruit_description), - style = Small14_Reg, - color = Gray004, - textAlign = TextAlign.Center, + style = Large20_Bold, + color = Color.Black, ) - Spacer(modifier = Modifier.height(32.dp)) + Spacer(modifier = Modifier.height(28.dp)) Text( - text = stringResource(id = R.string.mate_recruit_title), + text = stringResource(id = R.string.trip_location), style = Medium16_SemiBold, color = Gray001, ) Spacer(modifier = Modifier.height(8.dp)) - TripmateTextField( - text = uiState.birthDate, - onTextChange = { text -> onAction(MateRecruitUiAction.OnBirthDateUpdated(text)) }, - searchTextHintRes = R.string.mate_recruit_title_hint, - clearText = { onAction(MateRecruitUiAction.OnClearIconClicked) }, + Box( modifier = Modifier .fillMaxWidth() - .height(52.dp), - errorText = uiState.birthDateErrorText?.asString(context), - ) + .height(60.dp) + .clip(RoundedCornerShape(8.dp)) + .background(Gray010) + + ) { + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + .align(Alignment.CenterStart), + ) { + Text( + text = uiState.tripLocation, + style = Small14_SemiBold, + color = Gray001, + ) + Spacer(modifier = Modifier.height(2.dp)) + Text( + text = uiState.tripLocationAddress, + style = XSmall12_Reg, + color = Gray004, + ) + } + } + Spacer(modifier = Modifier.height(28.dp)) Text( - text = stringResource(id = R.string.trip_location), + text = stringResource(id = R.string.mate_recruit_title), style = Medium16_SemiBold, color = Gray001, ) Spacer(modifier = Modifier.height(8.dp)) TripmateTextField( - text = uiState.birthDate, - onTextChange = { text -> onAction(MateRecruitUiAction.OnBirthDateUpdated(text)) }, - searchTextHintRes = R.string.trip_location_hint, - clearText = { onAction(MateRecruitUiAction.OnClearIconClicked) }, + text = uiState.mateRecruitTitle, + onTextChange = { text -> onAction(MateRecruitUiAction.OnMateRecruitTitleUpdated(text)) }, + searchTextHintRes = R.string.mate_recruit_title_hint, modifier = Modifier .fillMaxWidth() .height(52.dp), - errorText = uiState.birthDateErrorText?.asString(context), + maxLength = 25, ) + Spacer(modifier = Modifier.height(28.dp)) Text( text = stringResource(id = R.string.schedule), style = Medium16_SemiBold, color = Gray001, ) Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(28.dp)) Text( - text = stringResource(id = R.string.mate_recruit_title), + text = stringResource(id = R.string.mate_recruit_content), style = Medium16_SemiBold, color = Gray001, ) Spacer(modifier = Modifier.height(8.dp)) TripmateTextField( - text = uiState.birthDate, - onTextChange = { text -> onAction(MateRecruitUiAction.OnBirthDateUpdated(text)) }, - searchTextHintRes = R.string.mate_recruit_title_hint, - clearText = { onAction(MateRecruitUiAction.OnClearIconClicked) }, + text = uiState.mateRecruitContent, + onTextChange = { text -> onAction(MateRecruitUiAction.OnMateRecruitContentUpdated(text)) }, + searchTextHintRes = R.string.mate_recruit_content_hint, modifier = Modifier .fillMaxWidth() .height(52.dp), - errorText = uiState.birthDateErrorText?.asString(context), + maxLength = 200, ) + Spacer(modifier = Modifier.height(28.dp)) Text( text = stringResource(id = R.string.mate_type), style = Medium16_SemiBold, color = Gray001, ) - Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(40.dp)) Text( text = stringResource(id = R.string.setting_gender_age), style = Medium16_SemiBold, color = Gray001, ) Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(40.dp)) Text( text = stringResource(id = R.string.open_kakao_link), style = Medium16_SemiBold, color = Gray001, ) - Spacer(modifier = Modifier.height(8.dp)) - } - TripmateButton( - onClick = {}, - modifier = Modifier - .align(Alignment.BottomCenter) - .fillMaxWidth() - .padding(horizontal = 32.dp, vertical = 20.dp), - contentPadding = PaddingValues(vertical = 17.dp), - enabled = true, - ) { + Spacer(modifier = Modifier.height(4.dp)) Text( - text = stringResource(R.string.done), - style = Medium16_SemiBold, + text = stringResource(id = R.string.open_kakao_link_description), + style = XSmall12_Reg, + color = Gray004, + ) + Spacer(modifier = Modifier.height(8.dp)) + TripmateTextField( + text = uiState.openKakaoLink, + onTextChange = { text -> onAction(MateRecruitUiAction.OnOpenKakaoLinkUpdated(text)) }, + searchTextHintRes = R.string.open_kakao_link_hint, + modifier = Modifier + .fillMaxWidth() + .height(52.dp), ) + Spacer(modifier = Modifier.height(40.dp)) + TripmateButton( + onClick = {}, + modifier = Modifier.fillMaxWidth(), + contentPadding = PaddingValues(vertical = 18.dp), + enabled = true, + ) { + Text( + text = stringResource(R.string.done), + style = Medium16_SemiBold, + ) + } + Spacer(modifier = Modifier.height(55.dp)) } } } diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt index 0f2db81d..308ee3d3 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt @@ -1,24 +1,20 @@ package com.tripmate.android.feature.recruit.viewmodel -import com.tripmate.android.domain.entity.TripStyleEntity +import com.tripmate.android.domain.entity.GenderAgeGroupEntity sealed interface MateRecruitUiAction { - data class OnTripStyleSelected(val tripStyle: TripStyleEntity) : MateRecruitUiAction - data class OnTripStyleDeselected(val tripStyle: TripStyleEntity) : MateRecruitUiAction - data class OnGenderSelected(val gender: Gender) : MateRecruitUiAction - data class OnBirthDateUpdated(val birthDate: String) : MateRecruitUiAction - data object OnClearIconClicked : MateRecruitUiAction - data class OnSelectClick(val screenType: ScreenType) : MateRecruitUiAction + data class OnMateRecruitTitleUpdated(val title: String) : MateRecruitUiAction + data class OnMateRecruitContentUpdated(val content: String) : MateRecruitUiAction + data class OnGenderAgeGroupSelected(val group: GenderAgeGroupEntity) : MateRecruitUiAction + data class OnGenderAgeGroupDeselected(val group: GenderAgeGroupEntity) : MateRecruitUiAction + data class OnMateTypeSelected(val mateType: MateType) : MateRecruitUiAction + data class OnOpenKakaoLinkUpdated(val link: String) : MateRecruitUiAction + data object OnDoneClick : MateRecruitUiAction } -enum class ScreenType { - QUESTION_1, - QUESTION_2, - QUESTION_3, - QUESTION_4, - TRIP_STYLE, - USER_INFO, - RESULT, +enum class MateType { + SIMILAR, + ALL, } enum class ErrorType { diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt index 13ff5107..a42b27a4 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt @@ -1,19 +1,15 @@ package com.tripmate.android.feature.recruit.viewmodel -import com.tripmate.android.core.common.UiText -import com.tripmate.android.domain.entity.TripStyleEntity -import com.tripmate.android.core.designsystem.R -import kotlinx.collections.immutable.ImmutableList +import com.tripmate.android.domain.entity.GenderAgeGroupEntity import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf data class MateRecruitUiState( - val selectedTripStyles: PersistentList = persistentListOf(), - val selectedGender: Gender = Gender.NOT_SPECIFIED, - val birthDate: String = "", - val birthDateErrorText: UiText? = null, + val mateRecruitTitle: String = "", + val tripLocation: String = "서퍼비치", + val tripLocationAddress: String = "강원도 양양군 현북면 하조대해안길 119", + val mateRecruitContent: String = "", + val mateType: MateType = MateType.ALL, + val selectedGenderAgeGroups: PersistentList = persistentListOf(), + val openKakaoLink: String = "", ) - -enum class Gender { - MALE, FEMALE, NOT_SPECIFIED -} diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt index 175d5ff5..a0ccfb4c 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt @@ -1,11 +1,8 @@ package com.tripmate.android.feature.recruit.viewmodel import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.tripmate.android.core.common.UiText -import com.tripmate.android.core.designsystem.R -import com.tripmate.android.domain.entity.TripStyleEntity -import com.tripmate.android.domain.repository.PersonalizationRepository +import com.tripmate.android.domain.entity.GenderAgeGroupEntity +import com.tripmate.android.domain.repository.MateRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -14,17 +11,12 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import kotlinx.datetime.Clock -import kotlinx.datetime.LocalDate -import kotlinx.datetime.TimeZone -import kotlinx.datetime.toLocalDateTime import javax.inject.Inject @HiltViewModel class MateRecruitViewModel @Inject constructor( @Suppress("UnusedPrivateProperty") - private val personalizationRepository: PersonalizationRepository, + private val mateRepository: MateRepository, ) : ViewModel() { private val _uiState = MutableStateFlow(MateRecruitUiState()) val uiState: StateFlow = _uiState.asStateFlow() @@ -32,96 +24,43 @@ class MateRecruitViewModel @Inject constructor( private val _uiEvent = Channel() val uiEvent: Flow = _uiEvent.receiveAsFlow() - enum class BirthDateValidationResult { - VALID, - INVALID_DATE, - UNDERAGE, - } - fun onAction(action: MateRecruitUiAction) { when (action) { - is MateRecruitUiAction.OnTripStyleSelected -> addSelectedTripStyle(action.tripStyle) - is MateRecruitUiAction.OnTripStyleDeselected -> removeSelectedTripStyle(action.tripStyle) - is MateRecruitUiAction.OnGenderSelected -> setGender(action.gender) - is MateRecruitUiAction.OnBirthDateUpdated -> setBirthDate(action.birthDate) - is MateRecruitUiAction.OnClearIconClicked -> clearText() - is MateRecruitUiAction.OnSelectClick -> {} + is MateRecruitUiAction.OnMateRecruitTitleUpdated -> setMateRecruitTitle(action.title) + is MateRecruitUiAction.OnGenderAgeGroupSelected -> addGenderAgeGroup(action.group) + is MateRecruitUiAction.OnGenderAgeGroupDeselected -> removeSelectedTripStyle(action.group) + is MateRecruitUiAction.OnMateTypeSelected -> setMateType(action.mateType) + is MateRecruitUiAction.OnMateRecruitContentUpdated -> setMateRecruitContent(action.content) + is MateRecruitUiAction.OnOpenKakaoLinkUpdated -> setOpenKakaoLink(action.link) + is MateRecruitUiAction.OnDoneClick -> {} } } - private fun addSelectedTripStyle(tripStyle: TripStyleEntity) { - if (_uiState.value.selectedTripStyles.size >= 3) { - viewModelScope.launch {} - return - } - _uiState.update { - it.copy(selectedTripStyles = it.selectedTripStyles.add(tripStyle)) - } + private fun setMateRecruitTitle(title: String) { + _uiState.update { it.copy(mateRecruitTitle = title) } } - private fun removeSelectedTripStyle(tripStyle: TripStyleEntity) { - _uiState.update { - it.copy(selectedTripStyles = it.selectedTripStyles.remove(tripStyle)) - } + private fun setMateRecruitContent(content: String) { + _uiState.update { it.copy(mateRecruitContent = content) } } - private fun setGender(gender: Gender) { - _uiState.update { it.copy(selectedGender = gender) } + private fun setMateType(mateType: MateType) { + _uiState.update { it.copy(mateType = mateType) } } - private fun setBirthDate(birthDate: String) { - if (birthDate.length <= 6) { - _uiState.update { it.copy(birthDate = birthDate) } - if (birthDate.length == 6) { - if (validateBirthDate(birthDate) == BirthDateValidationResult.INVALID_DATE) { - _uiState.update { it.copy(birthDateErrorText = null) } - } else { - _uiState.update { it.copy(birthDateErrorText = null) } - } - } + private fun addGenderAgeGroup(genderAgeGroup: GenderAgeGroupEntity) { + _uiState.update { + it.copy(selectedGenderAgeGroups = it.selectedGenderAgeGroups.add(genderAgeGroup)) } } - private fun clearText() { + private fun removeSelectedTripStyle(genderAgeGroup: GenderAgeGroupEntity) { _uiState.update { - it.copy( - birthDate = "", - birthDateErrorText = null, - ) + it.copy(selectedGenderAgeGroups = it.selectedGenderAgeGroups.remove(genderAgeGroup)) } } - @Suppress("SwallowedException") - private fun validateBirthDate(birthDate: String): BirthDateValidationResult { - // 숫자로 만 이루어져 있는지 체크 - if (!birthDate.matches(Regex("[0-9]+"))) return BirthDateValidationResult.INVALID_DATE - - // 유효한 날짜인지 체크 - val year = birthDate.substring(0, 2).toInt() - val month = birthDate.substring(2, 4).toInt() - val day = birthDate.substring(4, 6).toInt() - - if (month < 1 || month > 12 || day < 1 || day > 31) return BirthDateValidationResult.INVALID_DATE - - // 현재 날짜 기준으로 만 19세 이상인지 체크 - val currentYear = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).year % 100 - val fullYear = if (year <= currentYear) 2000 + year else 1900 + year - - try { - val birthDateFormatted = LocalDate(fullYear, month, day) - val today = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date - val age = today.year - birthDateFormatted.year - - return if (age > 19) { - BirthDateValidationResult.VALID - } else if (age == 19) { - val birthDayThisYear = LocalDate(today.year, birthDateFormatted.month, birthDateFormatted.dayOfMonth) - if (birthDayThisYear <= today) BirthDateValidationResult.VALID else BirthDateValidationResult.UNDERAGE - } else { - BirthDateValidationResult.UNDERAGE - } - } catch (e: IllegalArgumentException) { - return BirthDateValidationResult.INVALID_DATE - } + private fun setOpenKakaoLink(link: String) { + _uiState.update { it.copy(openKakaoLink = link) } } } From 42795a05ac969caddcba205f2b41450bb3f6f8fd Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Fri, 30 Aug 2024 04:47:47 +0900 Subject: [PATCH 06/22] =?UTF-8?q?[style]=20=EB=8F=99=ED=96=89=20=EB=AA=A8?= =?UTF-8?q?=EC=A7=91=20=ED=99=94=EB=A9=B4=20UI=20=EA=B5=AC=EC=84=B1=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/designsystem/component/TextField.kt | 38 ++++++-- .../src/main/res/values/strings.xml | 15 --- .../feature/recruit/MateRecruitScreen.kt | 97 ++++++++++++++++++- .../recruit/component/MateRecruitCheckBox.kt | 73 ++++++++++++++ .../src/main/res/drawable/ic_mate_setting.xml | 15 +++ .../res/drawable/ic_mate_setting_checked.xml | 15 +++ .../src/main/res/drawable/ic_mate_type.xml | 14 +++ .../res/drawable/ic_mate_type_checked.xml | 11 +++ .../src/main/res/values/strings.xml | 23 +++++ 9 files changed, 273 insertions(+), 28 deletions(-) create mode 100644 feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/MateRecruitCheckBox.kt create mode 100644 feature/mate-recruit/src/main/res/drawable/ic_mate_setting.xml create mode 100644 feature/mate-recruit/src/main/res/drawable/ic_mate_setting_checked.xml create mode 100644 feature/mate-recruit/src/main/res/drawable/ic_mate_type.xml create mode 100644 feature/mate-recruit/src/main/res/drawable/ic_mate_type_checked.xml create mode 100644 feature/mate-recruit/src/main/res/values/strings.xml diff --git a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt index 5789612e..2a05f86d 100644 --- a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt +++ b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt @@ -5,15 +5,18 @@ import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable +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.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -24,14 +27,15 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp +import com.tripmate.android.core.designsystem.ComponentPreview import com.tripmate.android.core.designsystem.R import com.tripmate.android.core.designsystem.theme.Error import com.tripmate.android.core.designsystem.theme.Gray001 import com.tripmate.android.core.designsystem.theme.Gray006 import com.tripmate.android.core.designsystem.theme.Gray008 import com.tripmate.android.core.designsystem.theme.Small14_Reg +import com.tripmate.android.core.designsystem.theme.TripmateTheme import com.tripmate.android.core.designsystem.theme.XSmall12_Reg @Composable @@ -52,11 +56,10 @@ fun TripmateTextField( BasicTextField( value = text, onValueChange = onTextChange, - modifier = Modifier.height(52.dp), - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Number, - imeAction = ImeAction.Done, - ), + modifier = Modifier + .fillMaxWidth() + .height(52.dp), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), textStyle = Small14_Reg.copy(color = textColor), singleLine = true, decorationBox = { innerTextField -> @@ -73,17 +76,17 @@ fun TripmateTextField( verticalAlignment = Alignment.CenterVertically, ) { Spacer(modifier = Modifier.width(20.dp)) - Box(modifier = Modifier.weight(1f)) { + Box(modifier = Modifier.weight(1f, fill = false)) { if (text.isEmpty()) { Text( text = stringResource(id = searchTextHintRes), color = Gray006, + maxLines = 1, style = Small14_Reg, ) } innerTextField() } - Spacer(modifier = Modifier.weight(1f)) if (text.isNotEmpty()) { Icon( imageVector = ImageVector.vectorResource(R.drawable.ic_clear_text), @@ -100,7 +103,11 @@ fun TripmateTextField( }, ) Spacer(modifier = Modifier.height(4.dp)) - Row { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { if (errorText != null) { Spacer(modifier = Modifier.height(4.dp)) Text( @@ -120,3 +127,16 @@ fun TripmateTextField( } } } + +@ComponentPreview +@Composable +fun TripmateTextFieldPreview() { + TripmateTheme { + TripmateTextField( + text = "트립메이트", + onTextChange = {}, + searchTextHintRes = R.string.app_name, + ) + } +} + diff --git a/core/designsystem/src/main/res/values/strings.xml b/core/designsystem/src/main/res/values/strings.xml index 10fe5758..f8859d43 100644 --- a/core/designsystem/src/main/res/values/strings.xml +++ b/core/designsystem/src/main/res/values/strings.xml @@ -16,16 +16,6 @@ 알림 아직 알림이 없습니다. - 동행 모집 제목 - ex)서퍼비치에서 점심 같이드실분 - 여행 장소 - ex)양양 서퍼비치 - 일정 - 동행 유형 - 작성 완료 - 모든 항목을 필수로 입력해주세요😉 - 성별, 연령대 설정 - 오픈 카톡방 링크 전체 @@ -37,10 +27,5 @@ 지도 보기 동행 모집만 보기 동행 모집 - 동행 글쓰기 - 동행 모집 내용 - 동행 모집 내용을 작성해주세요. - 오픈 채팅방 개설 후 링크를 입력해주세요 - 오픈 카톡방을 만든 후 작성해주세요 diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt index 9f2c4d5e..a022e057 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt @@ -1,14 +1,19 @@ package com.tripmate.android.feature.recruit import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll @@ -25,13 +30,14 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tripmate.android.core.common.ObserveAsEvents -import com.tripmate.android.core.designsystem.R import com.tripmate.android.core.designsystem.component.TopAppBarNavigationType import com.tripmate.android.core.designsystem.component.TripmateButton import com.tripmate.android.core.designsystem.component.TripmateTextField import com.tripmate.android.core.designsystem.component.TripmateTopAppBar import com.tripmate.android.core.designsystem.theme.Gray001 import com.tripmate.android.core.designsystem.theme.Gray004 +import com.tripmate.android.core.designsystem.theme.Gray007 +import com.tripmate.android.core.designsystem.theme.Gray008 import com.tripmate.android.core.designsystem.theme.Gray010 import com.tripmate.android.core.designsystem.theme.Large20_Bold import com.tripmate.android.core.designsystem.theme.Medium16_SemiBold @@ -39,9 +45,11 @@ import com.tripmate.android.core.designsystem.theme.Small14_SemiBold import com.tripmate.android.core.designsystem.theme.TripmateTheme import com.tripmate.android.core.designsystem.theme.XSmall12_Reg import com.tripmate.android.core.ui.DevicePreview +import com.tripmate.android.feature.recruit.component.MateRecruitCheckBox import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiAction import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiState import com.tripmate.android.feature.recruit.viewmodel.MateRecruitViewModel +import com.tripmate.android.feature.mate_recruit.R @Composable fun MateRecruitRoute( @@ -120,9 +128,9 @@ fun MateRecruitContent( .fillMaxWidth() .height(60.dp) .clip(RoundedCornerShape(8.dp)) - .background(Gray010) + .background(Gray010), - ) { + ) { Column( modifier = Modifier .padding(horizontal = 16.dp) @@ -133,7 +141,7 @@ fun MateRecruitContent( style = Small14_SemiBold, color = Gray001, ) - Spacer(modifier = Modifier.height(2.dp)) + Spacer(modifier = Modifier.height(4.dp)) Text( text = uiState.tripLocationAddress, style = XSmall12_Reg, @@ -164,6 +172,37 @@ fun MateRecruitContent( color = Gray001, ) Spacer(modifier = Modifier.height(8.dp)) + Row { + Box( + modifier = Modifier + .weight(1f) + .border(1.dp, Gray008, RoundedCornerShape(8.dp)) + .clip(RoundedCornerShape(8.dp)) + .padding(vertical = 16.dp, horizontal = 12.dp) + .clickable {}, + ) { + Text( + text = "2024년 08월 03일", + style = Small14_SemiBold, + color = Gray007, + ) + } + Spacer(modifier = Modifier.width(8.dp)) + Box( + modifier = Modifier + .weight(1f) + .border(1.dp, Gray008, RoundedCornerShape(8.dp)) + .clip(RoundedCornerShape(8.dp)) + .padding(vertical = 16.dp, horizontal = 12.dp) + .clickable {}, + ) { + Text( + text = "오전 12시 00분", + style = Small14_SemiBold, + color = Gray007, + ) + } + } Spacer(modifier = Modifier.height(28.dp)) Text( text = stringResource(id = R.string.mate_recruit_content), @@ -186,12 +225,62 @@ fun MateRecruitContent( style = Medium16_SemiBold, color = Gray001, ) + Spacer(modifier = Modifier.height(12.dp)) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + MateRecruitCheckBox( + text = stringResource(R.string.similar_mate_type), + isSelected = true, + onSelectedChange = {}, + iconRes = R.drawable.ic_mate_type, + checkedIconRes = R.drawable.ic_mate_type_checked, + ) + Spacer(modifier = Modifier.height(10.dp)) + MateRecruitCheckBox( + text = stringResource(R.string.all_mate_type), + isSelected = true, + onSelectedChange = {}, + iconRes = R.drawable.ic_mate_type, + checkedIconRes = R.drawable.ic_mate_type_checked, + ) + } Spacer(modifier = Modifier.height(40.dp)) Text( text = stringResource(id = R.string.setting_gender_age), style = Medium16_SemiBold, color = Gray001, ) + Spacer(modifier = Modifier.height(12.dp)) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + MateRecruitCheckBox( + text = stringResource(R.string.same_gender), + isSelected = true, + onSelectedChange = {}, + iconRes = R.drawable.ic_mate_setting, + checkedIconRes = R.drawable.ic_mate_setting_checked, + ) + Spacer(modifier = Modifier.height(10.dp)) + MateRecruitCheckBox( + text = stringResource(R.string.same_age), + isSelected = true, + onSelectedChange = {}, + iconRes = R.drawable.ic_mate_setting, + checkedIconRes = R.drawable.ic_mate_setting_checked, + ) + Spacer(modifier = Modifier.height(10.dp)) + MateRecruitCheckBox( + text = stringResource(R.string.no_matter), + isSelected = true, + onSelectedChange = {}, + iconRes = R.drawable.ic_mate_setting, + checkedIconRes = R.drawable.ic_mate_setting_checked, + ) + } Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(40.dp)) Text( diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/MateRecruitCheckBox.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/MateRecruitCheckBox.kt new file mode 100644 index 00000000..ef79f633 --- /dev/null +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/MateRecruitCheckBox.kt @@ -0,0 +1,73 @@ +package com.tripmate.android.feature.recruit.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +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.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.unit.dp +import com.tripmate.android.core.designsystem.ComponentPreview +import com.tripmate.android.core.designsystem.theme.Background02 +import com.tripmate.android.core.designsystem.theme.Background03 +import com.tripmate.android.core.designsystem.theme.Medium16_Reg +import com.tripmate.android.core.designsystem.theme.Primary03 +import com.tripmate.android.feature.mate_recruit.R + +@Composable +fun MateRecruitCheckBox( + text: String, + isSelected: Boolean, + onSelectedChange: () -> Unit, + iconRes: Int, + checkedIconRes: Int, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .fillMaxWidth() + .clickable { onSelectedChange() }, + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + imageVector = if (isSelected) { + ImageVector.vectorResource(checkedIconRes) + } else { + ImageVector.vectorResource(iconRes) + }, + contentDescription = "Check Icon", + tint = Color.Unspecified, + ) + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = text, + style = Medium16_Reg, + modifier = Modifier.weight(1f), + ) + } +} + +@ComponentPreview +@Composable +fun MateRecruitCheckBoxPreview() { + MateRecruitCheckBox( + text = "니와 비슷한 유형의 동행 찾기", + isSelected = false, + onSelectedChange = {}, + iconRes = R.drawable.ic_mate_type, + checkedIconRes = R.drawable.ic_mate_type_checked, + ) +} diff --git a/feature/mate-recruit/src/main/res/drawable/ic_mate_setting.xml b/feature/mate-recruit/src/main/res/drawable/ic_mate_setting.xml new file mode 100644 index 00000000..5c2da3d4 --- /dev/null +++ b/feature/mate-recruit/src/main/res/drawable/ic_mate_setting.xml @@ -0,0 +1,15 @@ + + + + diff --git a/feature/mate-recruit/src/main/res/drawable/ic_mate_setting_checked.xml b/feature/mate-recruit/src/main/res/drawable/ic_mate_setting_checked.xml new file mode 100644 index 00000000..f482cabb --- /dev/null +++ b/feature/mate-recruit/src/main/res/drawable/ic_mate_setting_checked.xml @@ -0,0 +1,15 @@ + + + + diff --git a/feature/mate-recruit/src/main/res/drawable/ic_mate_type.xml b/feature/mate-recruit/src/main/res/drawable/ic_mate_type.xml new file mode 100644 index 00000000..c04747be --- /dev/null +++ b/feature/mate-recruit/src/main/res/drawable/ic_mate_type.xml @@ -0,0 +1,14 @@ + + + + diff --git a/feature/mate-recruit/src/main/res/drawable/ic_mate_type_checked.xml b/feature/mate-recruit/src/main/res/drawable/ic_mate_type_checked.xml new file mode 100644 index 00000000..b4846abd --- /dev/null +++ b/feature/mate-recruit/src/main/res/drawable/ic_mate_type_checked.xml @@ -0,0 +1,11 @@ + + + diff --git a/feature/mate-recruit/src/main/res/values/strings.xml b/feature/mate-recruit/src/main/res/values/strings.xml new file mode 100644 index 00000000..c149dd22 --- /dev/null +++ b/feature/mate-recruit/src/main/res/values/strings.xml @@ -0,0 +1,23 @@ + + + 동행 모집 제목 + ex)서퍼비치에서 점심 같이드실분 + 여행 장소 + ex)양양 서퍼비치 + 동행 글쓰기 + 일정 + 동행 모집 내용 + 동행 모집 내용을 작성해주세요. + 동행 유형 + 작성 완료 + 모든 항목을 필수로 입력해주세요😉 + 성별, 연령대 설정 + 오픈 카톡방 링크 + 오픈 카톡방을 만든 후 작성해주세요 + 오픈 채팅방 개설 후 링크를 입력해주세요 + 나와 비슷한 유형의 동행 찾기 + 모든 유형 동행 찾기 + 같은 성별 원해요 + 동일한 연령대 원해요 + 상관 없어요 + From 60425fc17f2714bb1e0202389f7c3b7e23f28c83 Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Fri, 30 Aug 2024 15:11:53 +0900 Subject: [PATCH 07/22] =?UTF-8?q?[feat]=20=EB=8F=99=ED=96=89=20=EB=AA=A8?= =?UTF-8?q?=EC=A7=91=20=ED=99=94=EB=A9=B4=20=EC=B2=B4=ED=81=AC=20=EB=B0=95?= =?UTF-8?q?=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/recruit/MateRecruitScreen.kt | 58 +++++++++---------- .../recruit/component/MateRecruitCheckBox.kt | 19 +++--- .../recruit/viewmodel/MateRecruitUiAction.kt | 2 +- .../recruit/viewmodel/MateRecruitUiState.kt | 8 ++- .../recruit/viewmodel/MateRecruitViewModel.kt | 4 +- .../src/main/res/drawable/ic_mate_type.xml | 5 +- .../res/drawable/ic_mate_type_checked.xml | 5 +- 7 files changed, 49 insertions(+), 52 deletions(-) diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt index a022e057..2eb57baf 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll @@ -50,6 +51,7 @@ import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiAction import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiState import com.tripmate.android.feature.recruit.viewmodel.MateRecruitViewModel import com.tripmate.android.feature.mate_recruit.R +import com.tripmate.android.feature.recruit.viewmodel.MateType @Composable fun MateRecruitRoute( @@ -232,16 +234,16 @@ fun MateRecruitContent( ) { MateRecruitCheckBox( text = stringResource(R.string.similar_mate_type), - isSelected = true, - onSelectedChange = {}, + isSelected = uiState.selectedMateType == MateType.SIMILAR, + onSelectedChange = { onAction(MateRecruitUiAction.OnMateTypeSelected(MateType.SIMILAR)) }, iconRes = R.drawable.ic_mate_type, checkedIconRes = R.drawable.ic_mate_type_checked, ) Spacer(modifier = Modifier.height(10.dp)) MateRecruitCheckBox( text = stringResource(R.string.all_mate_type), - isSelected = true, - onSelectedChange = {}, + isSelected = uiState.selectedMateType == MateType.ALL, + onSelectedChange = { onAction(MateRecruitUiAction.OnMateTypeSelected(MateType.ALL)) }, iconRes = R.drawable.ic_mate_type, checkedIconRes = R.drawable.ic_mate_type_checked, ) @@ -253,35 +255,29 @@ fun MateRecruitContent( color = Gray001, ) Spacer(modifier = Modifier.height(12.dp)) - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, + LazyColumn( + modifier = modifier.height((uiState.allGenderAgeGroups.size * 32).dp), + verticalArrangement = Arrangement.spacedBy(10.dp), ) { - MateRecruitCheckBox( - text = stringResource(R.string.same_gender), - isSelected = true, - onSelectedChange = {}, - iconRes = R.drawable.ic_mate_setting, - checkedIconRes = R.drawable.ic_mate_setting_checked, - ) - Spacer(modifier = Modifier.height(10.dp)) - MateRecruitCheckBox( - text = stringResource(R.string.same_age), - isSelected = true, - onSelectedChange = {}, - iconRes = R.drawable.ic_mate_setting, - checkedIconRes = R.drawable.ic_mate_setting_checked, - ) - Spacer(modifier = Modifier.height(10.dp)) - MateRecruitCheckBox( - text = stringResource(R.string.no_matter), - isSelected = true, - onSelectedChange = {}, - iconRes = R.drawable.ic_mate_setting, - checkedIconRes = R.drawable.ic_mate_setting_checked, - ) + items( + count = uiState.allGenderAgeGroups.size, + key = { index -> uiState.allGenderAgeGroups[index].id }, + ) { index -> + MateRecruitCheckBox( + text = stringResource(id = uiState.allGenderAgeGroups[index].textResId), + isSelected = uiState.selectedGenderAgeGroups.contains(uiState.allGenderAgeGroups[index]), + onSelectedChange = { + if (uiState.selectedGenderAgeGroups.contains(uiState.allGenderAgeGroups[index])) { + onAction(MateRecruitUiAction.OnGenderAgeGroupDeselected(uiState.allGenderAgeGroups[index])) + } else { + onAction(MateRecruitUiAction.OnGenderAgeGroupSelected(uiState.allGenderAgeGroups[index])) + } + }, + iconRes = R.drawable.ic_mate_setting, + checkedIconRes = R.drawable.ic_mate_setting_checked, + ) + } } - Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(40.dp)) Text( text = stringResource(id = R.string.open_kakao_link), diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/MateRecruitCheckBox.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/MateRecruitCheckBox.kt index ef79f633..d4960a47 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/MateRecruitCheckBox.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/MateRecruitCheckBox.kt @@ -1,30 +1,21 @@ package com.tripmate.android.feature.recruit.component -import androidx.compose.foundation.background -import androidx.compose.foundation.border import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape 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.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp import com.tripmate.android.core.designsystem.ComponentPreview -import com.tripmate.android.core.designsystem.theme.Background02 -import com.tripmate.android.core.designsystem.theme.Background03 import com.tripmate.android.core.designsystem.theme.Medium16_Reg -import com.tripmate.android.core.designsystem.theme.Primary03 import com.tripmate.android.feature.mate_recruit.R @Composable @@ -39,7 +30,11 @@ fun MateRecruitCheckBox( Row( modifier = modifier .fillMaxWidth() - .clickable { onSelectedChange() }, + .clickable { + if (!isSelected) { + onSelectedChange() + } + }, verticalAlignment = Alignment.CenterVertically, ) { Icon( @@ -67,7 +62,7 @@ fun MateRecruitCheckBoxPreview() { text = "니와 비슷한 유형의 동행 찾기", isSelected = false, onSelectedChange = {}, - iconRes = R.drawable.ic_mate_type, - checkedIconRes = R.drawable.ic_mate_type_checked, + iconRes = R.drawable.ic_mate_type_checked, + checkedIconRes = R.drawable.ic_mate_type, ) } diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt index 308ee3d3..880c2f18 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt @@ -5,9 +5,9 @@ import com.tripmate.android.domain.entity.GenderAgeGroupEntity sealed interface MateRecruitUiAction { data class OnMateRecruitTitleUpdated(val title: String) : MateRecruitUiAction data class OnMateRecruitContentUpdated(val content: String) : MateRecruitUiAction + data class OnMateTypeSelected(val mateType: MateType) : MateRecruitUiAction data class OnGenderAgeGroupSelected(val group: GenderAgeGroupEntity) : MateRecruitUiAction data class OnGenderAgeGroupDeselected(val group: GenderAgeGroupEntity) : MateRecruitUiAction - data class OnMateTypeSelected(val mateType: MateType) : MateRecruitUiAction data class OnOpenKakaoLinkUpdated(val link: String) : MateRecruitUiAction data object OnDoneClick : MateRecruitUiAction } diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt index a42b27a4..aff1dd34 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt @@ -1,6 +1,7 @@ package com.tripmate.android.feature.recruit.viewmodel import com.tripmate.android.domain.entity.GenderAgeGroupEntity +import com.tripmate.android.feature.mate_recruit.R import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf @@ -9,7 +10,12 @@ data class MateRecruitUiState( val tripLocation: String = "서퍼비치", val tripLocationAddress: String = "강원도 양양군 현북면 하조대해안길 119", val mateRecruitContent: String = "", - val mateType: MateType = MateType.ALL, + val selectedMateType: MateType = MateType.ALL, + val allGenderAgeGroups: PersistentList = persistentListOf( + GenderAgeGroupEntity(id = 1, textResId = R.string.same_gender, isSelected = false), + GenderAgeGroupEntity(id = 2, textResId = R.string.same_age, isSelected = false), + GenderAgeGroupEntity(id = 3, textResId = R.string.no_matter, isSelected = false), + ), val selectedGenderAgeGroups: PersistentList = persistentListOf(), val openKakaoLink: String = "", ) diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt index a0ccfb4c..ef665fa2 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt @@ -27,9 +27,9 @@ class MateRecruitViewModel @Inject constructor( fun onAction(action: MateRecruitUiAction) { when (action) { is MateRecruitUiAction.OnMateRecruitTitleUpdated -> setMateRecruitTitle(action.title) + is MateRecruitUiAction.OnMateTypeSelected -> setMateType(action.mateType) is MateRecruitUiAction.OnGenderAgeGroupSelected -> addGenderAgeGroup(action.group) is MateRecruitUiAction.OnGenderAgeGroupDeselected -> removeSelectedTripStyle(action.group) - is MateRecruitUiAction.OnMateTypeSelected -> setMateType(action.mateType) is MateRecruitUiAction.OnMateRecruitContentUpdated -> setMateRecruitContent(action.content) is MateRecruitUiAction.OnOpenKakaoLinkUpdated -> setOpenKakaoLink(action.link) is MateRecruitUiAction.OnDoneClick -> {} @@ -45,7 +45,7 @@ class MateRecruitViewModel @Inject constructor( } private fun setMateType(mateType: MateType) { - _uiState.update { it.copy(mateType = mateType) } + _uiState.update { it.copy(selectedMateType = mateType) } } private fun addGenderAgeGroup(genderAgeGroup: GenderAgeGroupEntity) { diff --git a/feature/mate-recruit/src/main/res/drawable/ic_mate_type.xml b/feature/mate-recruit/src/main/res/drawable/ic_mate_type.xml index c04747be..b4846abd 100644 --- a/feature/mate-recruit/src/main/res/drawable/ic_mate_type.xml +++ b/feature/mate-recruit/src/main/res/drawable/ic_mate_type.xml @@ -7,8 +7,5 @@ android:strokeWidth="1" android:pathData="M12,12m-8.5,0a8.5,8.5 0,1 1,17 0a8.5,8.5 0,1 1,-17 0" android:fillColor="#00000000" - android:strokeColor="#205EFF"/> - + android:strokeColor="#C4C7CA"/> diff --git a/feature/mate-recruit/src/main/res/drawable/ic_mate_type_checked.xml b/feature/mate-recruit/src/main/res/drawable/ic_mate_type_checked.xml index b4846abd..c04747be 100644 --- a/feature/mate-recruit/src/main/res/drawable/ic_mate_type_checked.xml +++ b/feature/mate-recruit/src/main/res/drawable/ic_mate_type_checked.xml @@ -7,5 +7,8 @@ android:strokeWidth="1" android:pathData="M12,12m-8.5,0a8.5,8.5 0,1 1,17 0a8.5,8.5 0,1 1,-17 0" android:fillColor="#00000000" - android:strokeColor="#C4C7CA"/> + android:strokeColor="#205EFF"/> + From 1c894ec1d5d80ddbdce50749f1a14828749ce506 Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Sun, 1 Sep 2024 04:07:59 +0900 Subject: [PATCH 08/22] =?UTF-8?q?[chore]=20main=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=EB=A1=9C=20start=20Activity=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/login/src/main/AndroidManifest.xml | 6 ------ feature/main/src/main/AndroidManifest.xml | 8 +++++++- feature/personalization/src/main/AndroidManifest.xml | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/feature/login/src/main/AndroidManifest.xml b/feature/login/src/main/AndroidManifest.xml index bac8fe86..cffb6edb 100644 --- a/feature/login/src/main/AndroidManifest.xml +++ b/feature/login/src/main/AndroidManifest.xml @@ -9,12 +9,6 @@ android:screenOrientation="portrait" android:theme="@style/Theme.Tripmate"> - - - - - - diff --git a/feature/main/src/main/AndroidManifest.xml b/feature/main/src/main/AndroidManifest.xml index 394621f4..766b926d 100644 --- a/feature/main/src/main/AndroidManifest.xml +++ b/feature/main/src/main/AndroidManifest.xml @@ -5,9 +5,15 @@ + + + + + + diff --git a/feature/personalization/src/main/AndroidManifest.xml b/feature/personalization/src/main/AndroidManifest.xml index 302ceb37..49d8d9d4 100644 --- a/feature/personalization/src/main/AndroidManifest.xml +++ b/feature/personalization/src/main/AndroidManifest.xml @@ -4,7 +4,7 @@ From 6196cf2bd8e2663f6560c87d5e10a6073a946c0e Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Sun, 1 Sep 2024 04:08:49 +0900 Subject: [PATCH 09/22] =?UTF-8?q?[add]=20Wheel=20Pick=20Compose=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/mate-recruit/build.gradle.kts | 1 + gradle/libs.versions.toml | 4 +++- settings.gradle.kts | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/feature/mate-recruit/build.gradle.kts b/feature/mate-recruit/build.gradle.kts index fd4222dd..311d0e23 100644 --- a/feature/mate-recruit/build.gradle.kts +++ b/feature/mate-recruit/build.gradle.kts @@ -22,5 +22,6 @@ dependencies { libs.androidx.splash, libs.compose.system.ui.controller, libs.timber, + libs.wheel.picker.compose, ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0b741c62..82e1949e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -25,7 +25,7 @@ kotlinx-serialization-converter = "1.0.0" kotlinx-collections-immutable = "0.3.7" android-play-services-location = "21.3.0" -accompanist-permissions = "0.35.0-alpha" +accompanist-permissions = "0.35.0-alpha" androidx-core = "1.13.1" androidx-lifecycle = "2.8.3" @@ -56,6 +56,7 @@ compose-stable-marker = "1.0.4" landscapist = "2.3.3" kakao-maps = "2.9.5" kakao-core = "2.20.4" +wheel-picker-compose = "1.1.11" [libraries] @@ -116,6 +117,7 @@ landscapist-coil = { group = "com.github.skydoves", name = "landscapist-coil" } landscapist-placeholder = { group = "com.github.skydoves", name = "landscapist-placeholder" } kakao-maps = { group = "com.kakao.maps.open", name = "android", version.ref = "kakao-maps" } kakao-auth = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakao-core" } +wheel-picker-compose = { group = "com.github.commandiron", name = "WheelPickerCompose", version.ref = "wheel-picker-compose" } [plugins] diff --git a/settings.gradle.kts b/settings.gradle.kts index 0d270d26..cb586b14 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,6 +18,7 @@ dependencyResolutionManagement { google() mavenCentral() maven { url = uri("https://devrepo.kakao.com/nexus/content/groups/public/") } + maven { url = uri("https://jitpack.io") } } } From e90ee5a9ba53fd13b7bc5cf46259f0097b600c3f Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Sun, 1 Sep 2024 04:09:33 +0900 Subject: [PATCH 10/22] =?UTF-8?q?[style]=20=EA=B0=9C=EC=9D=B8=ED=99=94=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../personalization/Question1Screen.kt | 4 +- .../personalization/Question2Screen.kt | 4 +- .../personalization/Question3Screen.kt | 4 +- .../personalization/Question4Screen.kt | 4 +- .../feature/personalization/ResultScreen.kt | 1 + .../feature/personalization/UserInfoScreen.kt | 16 +- .../component/AllTripStyles.kt | 147 ++++++++++++++++++ 7 files changed, 168 insertions(+), 12 deletions(-) create mode 100644 feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/AllTripStyles.kt diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question1Screen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question1Screen.kt index deb2c874..ab1c708b 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question1Screen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question1Screen.kt @@ -87,8 +87,8 @@ fun Question1Screen( }, modifier = Modifier .fillMaxWidth() - .padding(start = 32.dp, end = 32.dp, bottom = 62.dp) - .height(50.dp) + .padding(start = 16.dp, end = 16.dp, bottom = 62.dp) + .height(56.dp) .align(Alignment.BottomCenter), enabled = if (uiState.question1Answer == 0) false else true, ) { diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question2Screen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question2Screen.kt index 5780b98d..c74e2d4f 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question2Screen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question2Screen.kt @@ -84,8 +84,8 @@ fun Question2Screen( }, modifier = Modifier .fillMaxWidth() - .padding(start = 32.dp, end = 32.dp, bottom = 62.dp) - .height(50.dp) + .padding(start = 16.dp, end = 16.dp, bottom = 62.dp) + .height(56.dp) .align(Alignment.BottomCenter), enabled = if (uiState.question2Answer == 0) false else true, ) { diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question3Screen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question3Screen.kt index 40cc07e3..2a50b75d 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question3Screen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question3Screen.kt @@ -84,8 +84,8 @@ fun Question3Screen( }, modifier = Modifier .fillMaxWidth() - .padding(start = 32.dp, end = 32.dp, bottom = 62.dp) - .height(50.dp) + .padding(start = 16.dp, end = 16.dp, bottom = 62.dp) + .height(56.dp) .align(Alignment.BottomCenter), enabled = if (uiState.question3Answer == 0) false else true, ) { diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question4Screen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question4Screen.kt index e47ec31c..d12bb62f 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question4Screen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/Question4Screen.kt @@ -85,8 +85,8 @@ fun Question4Screen( }, modifier = Modifier .fillMaxWidth() - .padding(start = 32.dp, end = 32.dp, bottom = 62.dp) - .height(50.dp) + .padding(start = 16.dp, end = 16.dp, bottom = 62.dp) + .height(56.dp) .align(Alignment.BottomCenter), enabled = if (uiState.question4Answer == 0) false else true, ) { diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/ResultScreen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/ResultScreen.kt index 7df02498..c83b1852 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/ResultScreen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/ResultScreen.kt @@ -95,6 +95,7 @@ fun ResultScreen( modifier = Modifier .align(Alignment.BottomCenter) .fillMaxWidth() + .height(56.dp) .padding(horizontal = 32.dp, vertical = 20.dp), contentPadding = PaddingValues(vertical = 17.dp), ) { diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt index 8aefaee7..8b1a1a08 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -15,6 +16,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -36,6 +38,7 @@ import com.tripmate.android.feature.personalization.viewmodel.PersonalizationUiE import com.tripmate.android.feature.personalization.viewmodel.PersonalizationUiState import com.tripmate.android.feature.personalization.viewmodel.PersonalizationViewModel import com.tripmate.android.feature.personalization.viewmodel.ScreenType +import timber.log.Timber import kotlin.system.exitProcess @Composable @@ -45,7 +48,6 @@ fun UserInfoRoute( viewModel: PersonalizationViewModel, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - val context = LocalContext.current ObserveAsEvents(flow = viewModel.uiEvent) { event -> when (event) { @@ -143,13 +145,19 @@ fun UserInfoContent( Spacer(modifier = Modifier.height(8.dp)) TripmateTextField( text = uiState.birthDate, - onTextChange = { text -> onAction(PersonalizationUiAction.OnBirthDateUpdated(text)) }, + onTextChange = { text -> + onAction(PersonalizationUiAction.OnBirthDateUpdated(text)) + }, searchTextHintRes = R.string.birth_date_hint, - clearText = { onAction(PersonalizationUiAction.OnClearIconClicked) }, modifier = Modifier .fillMaxWidth() .height(52.dp), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + ), + clearText = { onAction(PersonalizationUiAction.OnClearIconClicked) }, errorText = uiState.birthDateErrorText?.asString(context), + isClearIconVisible = true, ) } } @@ -159,7 +167,7 @@ fun UserInfoContent( modifier = Modifier .align(Alignment.BottomCenter) .fillMaxWidth() - .padding(horizontal = 32.dp, vertical = 20.dp), + .padding(horizontal = 16.dp, vertical = 20.dp), contentPadding = PaddingValues(vertical = 17.dp), enabled = uiState.selectedGender != Gender.NOT_SPECIFIED && uiState.birthDate.length == 6 && uiState.birthDateErrorText == null, ) { diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/AllTripStyles.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/AllTripStyles.kt new file mode 100644 index 00000000..6ed59bfb --- /dev/null +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/component/AllTripStyles.kt @@ -0,0 +1,147 @@ +package com.tripmate.android.feature.personalization.component + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +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.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.tripmate.android.core.designsystem.ComponentPreview +import com.tripmate.android.core.designsystem.theme.Background02 +import com.tripmate.android.core.designsystem.theme.Background03 +import com.tripmate.android.core.designsystem.theme.Gray001 +import com.tripmate.android.core.designsystem.theme.Primary03 +import com.tripmate.android.core.designsystem.theme.Small14_SemiBold +import com.tripmate.android.core.designsystem.theme.TripmateTheme +import com.tripmate.android.domain.entity.TripStyleEntity +import com.tripmate.android.feature.personalization.R +import com.tripmate.android.feature.personalization.viewmodel.PersonalizationUiAction +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +@Composable +fun AllTripStyles( + tripStyles: ImmutableList, + selectedTripStyles: ImmutableList, + onAction: (PersonalizationUiAction) -> Unit, + modifier: Modifier = Modifier, +) { + LazyVerticalGrid( + columns = GridCells.Fixed(3), + modifier = modifier.height(if (tripStyles.isEmpty()) 0.dp else (((tripStyles.size - 1) / 3 + 1) * 140).dp), + verticalArrangement = Arrangement.spacedBy(7.dp), + horizontalArrangement = Arrangement.spacedBy(7.dp), + contentPadding = PaddingValues(bottom = 48.dp), + ) { + items( + count = tripStyles.size, + key = { index -> tripStyles[index].id }, + ) { index -> + TripStyleItem( + tripStyle = tripStyles[index], + isSelected = selectedTripStyles.contains(tripStyles[index]), + onSelectedChange = { + if (selectedTripStyles.contains(tripStyles[index])) { + onAction(PersonalizationUiAction.OnTripStyleDeselected(tripStyles[index])) + } else { + onAction(PersonalizationUiAction.OnTripStyleSelected(tripStyles[index])) + } + }, + ) + } + } +} + +@Composable +fun TripStyleItem( + tripStyle: TripStyleEntity, + isSelected: Boolean, + onSelectedChange: () -> Unit, +) { + Box( + modifier = Modifier + .width(100.dp) + .clip(RoundedCornerShape(8.dp)) + .background(if (isSelected) Background03 else Background02) + .border( + width = 1.dp, + color = if (isSelected) Primary03 else Background02, + shape = RoundedCornerShape(8.dp), + ) + .clickable { + onSelectedChange() + }, + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + ) { + Image( + painter = painterResource(id = tripStyle.imageResId), + contentDescription = null, + modifier = Modifier.size(48.dp), + ) + Spacer(modifier = Modifier.height(10.dp)) + Text( + text = stringResource(id = tripStyle.textResId), + color = Gray001, + style = Small14_SemiBold, + fontSize = 15.sp, + textAlign = TextAlign.Center, + ) + } + } +} + +@ComponentPreview +@Composable +fun AllTripStylesPreview() { + TripmateTheme { + AllTripStyles( + tripStyles = persistentListOf( + TripStyleEntity(1, R.string.trip_style_1, R.drawable.img_shopping_bags, false), + TripStyleEntity(2, R.string.trip_style_2, R.drawable.img_running_shoe, false), + TripStyleEntity(3, R.string.trip_style_3, R.drawable.img_statue_of_liberty, false), + TripStyleEntity(4, R.string.trip_style_4, R.drawable.img_diving_mask, false), + TripStyleEntity(5, R.string.trip_style_5, R.drawable.img_mobile_phone, false), + TripStyleEntity(6, R.string.trip_style_6, R.drawable.img_compass, false), + TripStyleEntity(7, R.string.trip_style_7, R.drawable.img_backpack, false), + TripStyleEntity(8, R.string.trip_style_8, R.drawable.img_camera_with_flash, false), + TripStyleEntity(9, R.string.trip_style_9, R.drawable.img_framed_picture, false), + TripStyleEntity(10, R.string.trip_style_10, R.drawable.img_pot_of_food, false), + TripStyleEntity(11, R.string.trip_style_11, R.drawable.img_deciduous_tree, false), + TripStyleEntity(12, R.string.trip_style_12, R.drawable.img_ferris_wheel, false), + ), + selectedTripStyles = persistentListOf( + TripStyleEntity(1, R.string.trip_style_1, R.drawable.img_shopping_bags, false), + TripStyleEntity(2, R.string.trip_style_2, R.drawable.img_running_shoe, false), + TripStyleEntity(3, R.string.trip_style_3, R.drawable.img_statue_of_liberty, false), + ), + onAction = {}, + ) + } +} From e64aa0da30dd31c313b42ef9f1eeb77b7827318d Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Sun, 1 Sep 2024 04:11:49 +0900 Subject: [PATCH 11/22] =?UTF-8?q?[refactor]=20=EA=B0=9C=EC=9D=B8=ED=99=94?= =?UTF-8?q?=20=ED=99=94=EB=A9=B4=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=ED=94=84=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../personalization/TripStyleScreen.kt | 102 +----------------- 1 file changed, 3 insertions(+), 99 deletions(-) diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/TripStyleScreen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/TripStyleScreen.kt index c26ba933..91e6aae6 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/TripStyleScreen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/TripStyleScreen.kt @@ -1,11 +1,6 @@ package com.tripmate.android.feature.personalization import android.widget.Toast -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -14,43 +9,31 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tripmate.android.core.common.ObserveAsEvents import com.tripmate.android.core.designsystem.component.TripmateButton -import com.tripmate.android.core.designsystem.theme.Background02 -import com.tripmate.android.core.designsystem.theme.Background03 import com.tripmate.android.core.designsystem.theme.Gray001 import com.tripmate.android.core.designsystem.theme.Large20_Bold import com.tripmate.android.core.designsystem.theme.Medium16_SemiBold -import com.tripmate.android.core.designsystem.theme.Primary03 import com.tripmate.android.core.designsystem.theme.Small14_Reg import com.tripmate.android.core.designsystem.theme.TripmateTheme import com.tripmate.android.core.ui.DevicePreview -import com.tripmate.android.domain.entity.TripStyleEntity +import com.tripmate.android.feature.personalization.component.AllTripStyles import com.tripmate.android.feature.personalization.viewmodel.PersonalizationUiAction import com.tripmate.android.feature.personalization.viewmodel.PersonalizationUiEvent import com.tripmate.android.feature.personalization.viewmodel.PersonalizationUiState import com.tripmate.android.feature.personalization.viewmodel.PersonalizationViewModel import com.tripmate.android.feature.personalization.viewmodel.ScreenType -import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @Composable @@ -97,7 +80,7 @@ fun TripStyleScreen( Column( modifier = Modifier .fillMaxSize() - .padding(bottom = 60.dp), + .padding(start = 8.dp, end = 8.dp, bottom = 60.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { Spacer(modifier = Modifier.height(70.dp)) @@ -126,7 +109,7 @@ fun TripStyleScreen( modifier = Modifier .align(Alignment.BottomCenter) .fillMaxWidth() - .padding(horizontal = 32.dp, vertical = 20.dp), + .padding(horizontal = 16.dp, vertical = 20.dp), contentPadding = PaddingValues(vertical = 17.dp), enabled = uiState.selectedTripStyles.isNotEmpty(), ) { @@ -138,85 +121,6 @@ fun TripStyleScreen( } } -@Composable -fun AllTripStyles( - tripStyles: ImmutableList, - selectedTripStyles: ImmutableList, - onAction: (PersonalizationUiAction) -> Unit, - modifier: Modifier = Modifier, -) { - LazyVerticalGrid( - columns = GridCells.Fixed(3), - modifier = modifier - .padding(horizontal = 8.dp) - .height(if (tripStyles.isEmpty()) 0.dp else (((tripStyles.size - 1) / 3 + 1) * 140).dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp), - contentPadding = PaddingValues(bottom = 48.dp), - ) { - items( - count = tripStyles.size, - key = { index -> tripStyles[index].id }, - ) { index -> - TripStyleItem( - tripStyle = tripStyles[index], - isSelected = selectedTripStyles.contains(tripStyles[index]), - onSelectedChange = { - if (selectedTripStyles.contains(tripStyles[index])) { - onAction(PersonalizationUiAction.OnTripStyleDeselected(tripStyles[index])) - } else { - onAction(PersonalizationUiAction.OnTripStyleSelected(tripStyles[index])) - } - }, - ) - } - } -} - -@Composable -fun TripStyleItem( - tripStyle: TripStyleEntity, - isSelected: Boolean, - onSelectedChange: () -> Unit, -) { - Box( - modifier = Modifier - .height(130.dp) - .width(100.dp) - .clip(RoundedCornerShape(8.dp)) - .background(if (isSelected) Background03 else Background02) - .border( - width = 1.dp, - color = if (isSelected) Primary03 else Background02, - shape = RoundedCornerShape(8.dp), - ) - .clickable { - onSelectedChange() - }, - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - ) { - Image( - painter = painterResource(id = tripStyle.imageResId), - contentDescription = null, - modifier = Modifier.size(48.dp), - ) - Spacer(modifier = Modifier.height(10.dp)) - Text( - text = stringResource(id = tripStyle.textResId), - color = Gray001, - style = Medium16_SemiBold, - fontSize = 15.sp, - ) - } - } -} - @DevicePreview @Composable fun TripStyleScreenPreview() { From b880d70a3978c6be8bdae7a76781039bf17a758f Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Sun, 1 Sep 2024 04:24:25 +0900 Subject: [PATCH 12/22] =?UTF-8?q?[style]=20Date,=20Time=20Picker=20BottomS?= =?UTF-8?q?heet=20UI=20=EA=B5=AC=EC=84=B1=20=EB=B0=8F=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/common/extension/LocalDate.kt | 10 + .../core/common/extension/LocalTime.kt | 12 ++ .../recruit/component/ScheduleBottomSheet.kt | 172 ++++++++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalDate.kt create mode 100644 core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalTime.kt create mode 100644 feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleBottomSheet.kt diff --git a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalDate.kt b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalDate.kt new file mode 100644 index 00000000..37871c84 --- /dev/null +++ b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalDate.kt @@ -0,0 +1,10 @@ +package com.tripmate.android.core.common.extension + +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import java.util.Locale + +fun LocalDate.formatToDate(): String { + val formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일", Locale.KOREAN) + return this.format(formatter) +} diff --git a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalTime.kt b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalTime.kt new file mode 100644 index 00000000..d540f280 --- /dev/null +++ b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalTime.kt @@ -0,0 +1,12 @@ +package com.tripmate.android.core.common.extension + +import java.time.LocalTime +import java.time.format.DateTimeFormatter +import java.util.Locale + +fun LocalTime.formatToTime(): String { + val formatter = DateTimeFormatter.ofPattern("a h시 mm분", Locale.KOREAN) + return this.format(formatter) + .replace("AM", "오전") + .replace("PM", "오후") +} diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleBottomSheet.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleBottomSheet.kt new file mode 100644 index 00000000..2e6e8a6d --- /dev/null +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleBottomSheet.kt @@ -0,0 +1,172 @@ +package com.tripmate.android.feature.recruit.component + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState +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.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.commandiron.wheel_picker_compose.WheelDatePicker +import com.commandiron.wheel_picker_compose.WheelTimePicker +import com.commandiron.wheel_picker_compose.core.TimeFormat +import com.commandiron.wheel_picker_compose.core.WheelPickerDefaults +import com.tripmate.android.core.common.extension.formatToDate +import com.tripmate.android.core.common.extension.formatToTime +import com.tripmate.android.core.common.extension.parseToLocalDate +import com.tripmate.android.core.common.extension.parseToLocalTime +import com.tripmate.android.core.designsystem.ComponentPreview +import com.tripmate.android.core.designsystem.component.TripmateButton +import com.tripmate.android.core.designsystem.theme.Background02 +import com.tripmate.android.core.designsystem.theme.Gray001 +import com.tripmate.android.core.designsystem.theme.Gray008 +import com.tripmate.android.core.designsystem.theme.Gray010 +import com.tripmate.android.core.designsystem.theme.Large20_Mid +import com.tripmate.android.core.designsystem.theme.Medium16_SemiBold +import com.tripmate.android.core.designsystem.theme.TripmateTheme +import com.tripmate.android.feature.mate_recruit.R +import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiAction +import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiState +import com.tripmate.android.feature.recruit.viewmodel.PickerType +import java.time.LocalDate +import java.time.LocalTime + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ScheduleBottomSheet( + pickerType: PickerType, + uiState: MateRecruitUiState, + onAction: (MateRecruitUiAction) -> Unit, +) { + val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + var selectedDate by remember { mutableStateOf("") } + var selectedTime by remember { mutableStateOf("") } + + ModalBottomSheet( + onDismissRequest = { + onAction(MateRecruitUiAction.OnDismiss(pickerType)) + }, + sheetState = bottomSheetState, + shape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp), + containerColor = Background02, + dragHandle = { + Column( + modifier = Modifier + .fillMaxWidth() + .background(Background02) + .padding(top = 10.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + HorizontalDivider( + thickness = 5.dp, + color = Gray008, + modifier = Modifier + .width(80.dp) + .clip(RoundedCornerShape(43.dp)), + ) + } + }, + windowInsets = WindowInsets(0, 0, 0, 0), + modifier = Modifier.wrapContentHeight(), + ) { + Column( + modifier = Modifier.navigationBarsPadding(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + if (pickerType == PickerType.DATE) { + WheelDatePicker( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 15.dp), + startDate = uiState.mateRecruitDate.parseToLocalDate(), + minDate = LocalDate.now(), + maxDate = LocalDate.of(2030, 12, 31), + yearsRange = IntRange(2024, 2030), + rowCount = 5, + textStyle = Large20_Mid, + textColor = Gray001, + selectorProperties = WheelPickerDefaults.selectorProperties( + enabled = true, + shape = RoundedCornerShape(10.dp), + color = Gray010, + border = BorderStroke(2.dp, Gray010), + ), + ) { snappedDate -> run { selectedDate = snappedDate.formatToDate() } } + } else { + WheelTimePicker( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 15.dp), + startTime = uiState.mateRecruitTime.parseToLocalTime(), + minTime = LocalTime.now(), + maxTime = LocalTime.of(23, 59), + timeFormat = TimeFormat.AM_PM, + rowCount = 5, + textStyle = Large20_Mid, + textColor = Gray001, + selectorProperties = WheelPickerDefaults.selectorProperties( + enabled = true, + shape = RoundedCornerShape(10.dp), + color = Gray010, + border = BorderStroke(2.dp, Gray010), + ), + ) { snappedTime -> run { selectedTime = snappedTime.formatToTime() } } + } + Spacer(modifier = Modifier.height(26.dp)) + TripmateButton( + onClick = { + if (pickerType == PickerType.DATE) { + onAction(MateRecruitUiAction.OnScheduleDateUpdated(selectedDate)) + } else { + onAction(MateRecruitUiAction.OnScheduleTimeUpdated(selectedTime)) + } + onAction(MateRecruitUiAction.OnDismiss(pickerType)) + }, + modifier = Modifier + .fillMaxWidth() + .height(56.dp) + .padding(horizontal = 15.dp), + ) { + Text( + text = stringResource(id = R.string.confirm), + style = Medium16_SemiBold, + color = Color.White, + ) + } + Spacer(modifier = Modifier.height(60.dp)) + } + } +} + +@ComponentPreview +@Composable +fun SchoolSearchBottomSheetPreview() { + TripmateTheme { + ScheduleBottomSheet( + pickerType = PickerType.DATE, + uiState = MateRecruitUiState(), + onAction = {}, + ) + } +} From 7b152a77a462851f7d7c99999db9edc71b0aa35f Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Sun, 1 Sep 2024 04:24:40 +0900 Subject: [PATCH 13/22] =?UTF-8?q?[feat]=20=EB=8F=99=ED=96=89=20=EB=AA=A8?= =?UTF-8?q?=EC=A7=91=20=ED=99=94=EB=A9=B4=20=EA=B5=AC=ED=98=84=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/core/common/extension/String.kt | 17 +++++++ .../core/designsystem/component/TextField.kt | 37 ++++++++------- .../android/core/designsystem/theme/Font.kt | 8 ++++ .../feature/recruit/MateRecruitScreen.kt | 35 +++++++++++---- .../recruit/viewmodel/MateRecruitUiAction.kt | 10 +++++ .../recruit/viewmodel/MateRecruitUiState.kt | 14 ++++++ .../recruit/viewmodel/MateRecruitViewModel.kt | 45 ++++++++++++++++++- .../src/main/res/values/strings.xml | 1 + 8 files changed, 140 insertions(+), 27 deletions(-) create mode 100644 core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt diff --git a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt new file mode 100644 index 00000000..01d1d11b --- /dev/null +++ b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt @@ -0,0 +1,17 @@ +package com.tripmate.android.core.common.extension + +import java.time.LocalDate +import java.time.LocalTime +import java.time.format.DateTimeFormatter +import java.util.Locale + +fun String.parseToLocalDate(): LocalDate { + val formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일", Locale.KOREAN) + return LocalDate.parse(this, formatter) +} + +fun String.parseToLocalTime(): LocalTime { + val formattedString = this.replace("오전", "AM").replace("오후", "PM") + val formatter = DateTimeFormatter.ofPattern("a h시 mm분", Locale.ENGLISH) + return LocalTime.parse(formattedString, formatter) +} diff --git a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt index 2a05f86d..42adb052 100644 --- a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt +++ b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt @@ -12,11 +12,10 @@ 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.width +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -44,27 +43,31 @@ fun TripmateTextField( onTextChange: (String) -> Unit, @StringRes searchTextHintRes: Int, modifier: Modifier = Modifier, + keyboardOptions: KeyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), clearText: () -> Unit = {}, errorText: String? = null, backgroundColor: Color = Color.White, textColor: Color = Gray001, cornerShape: RoundedCornerShape = RoundedCornerShape(6.dp), borderStroke: BorderStroke = BorderStroke(width = 1.dp, color = if (errorText != null) Error else Gray008), + multiline: Boolean = false, maxLength: Int? = null, + isClearIconVisible: Boolean = false, ) { - Column(modifier = modifier) { + Column { BasicTextField( value = text, - onValueChange = onTextChange, - modifier = Modifier - .fillMaxWidth() - .height(52.dp), - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + onValueChange = { text -> + if (maxLength != null && text.length <= maxLength) { + onTextChange(text) + } + }, + keyboardOptions = keyboardOptions, textStyle = Small14_Reg.copy(color = textColor), - singleLine = true, + singleLine = !multiline, decorationBox = { innerTextField -> Row( - modifier = Modifier + modifier = modifier .background( color = backgroundColor, shape = cornerShape, @@ -72,11 +75,11 @@ fun TripmateTextField( .border( border = borderStroke, shape = cornerShape, - ), - verticalAlignment = Alignment.CenterVertically, + ) + .padding(vertical = if (multiline) 12.dp else 0.dp, horizontal = 12.dp), + verticalAlignment = if (multiline) Alignment.Top else Alignment.CenterVertically, ) { - Spacer(modifier = Modifier.width(20.dp)) - Box(modifier = Modifier.weight(1f, fill = false)) { + Box { if (text.isEmpty()) { Text( text = stringResource(id = searchTextHintRes), @@ -87,7 +90,8 @@ fun TripmateTextField( } innerTextField() } - if (text.isNotEmpty()) { + Spacer(modifier = Modifier.weight(1f)) + if (text.isNotEmpty() && isClearIconVisible) { Icon( imageVector = ImageVector.vectorResource(R.drawable.ic_clear_text), contentDescription = "clear icon", @@ -98,7 +102,6 @@ fun TripmateTextField( }, ) } - Spacer(modifier = Modifier.width(20.dp)) } }, ) @@ -106,7 +109,7 @@ fun TripmateTextField( Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { if (errorText != null) { Spacer(modifier = Modifier.height(4.dp)) diff --git a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/theme/Font.kt b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/theme/Font.kt index 53af7017..4d59a2cf 100644 --- a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/theme/Font.kt +++ b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/theme/Font.kt @@ -54,6 +54,14 @@ val Large20_SemiBold = TextStyle( lineHeight = 28.sp, ) +val Large20_Mid = TextStyle( + fontFamily = pretendardFamily, + fontWeight = FontWeight.Normal, + fontSize = 20.sp, + letterSpacing = 0.sp, + lineHeight = 28.sp, +) + val Large20_Reg = TextStyle( fontFamily = pretendardFamily, fontWeight = FontWeight.Normal, diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt index 2eb57baf..298788a9 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt @@ -46,12 +46,14 @@ import com.tripmate.android.core.designsystem.theme.Small14_SemiBold import com.tripmate.android.core.designsystem.theme.TripmateTheme import com.tripmate.android.core.designsystem.theme.XSmall12_Reg import com.tripmate.android.core.ui.DevicePreview +import com.tripmate.android.feature.mate_recruit.R import com.tripmate.android.feature.recruit.component.MateRecruitCheckBox +import com.tripmate.android.feature.recruit.component.ScheduleBottomSheet import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiAction import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiState import com.tripmate.android.feature.recruit.viewmodel.MateRecruitViewModel -import com.tripmate.android.feature.mate_recruit.R import com.tripmate.android.feature.recruit.viewmodel.MateType +import com.tripmate.android.feature.recruit.viewmodel.PickerType @Composable fun MateRecruitRoute( @@ -96,6 +98,22 @@ fun MateRecruitScreen( onAction = onAction, ) } + + if (uiState.isDatePickerVisible) { + ScheduleBottomSheet( + pickerType = PickerType.DATE, + uiState = uiState, + onAction = onAction, + ) + } + + if (uiState.isTimePickerVisible) { + ScheduleBottomSheet( + pickerType = PickerType.TIME, + uiState = uiState, + onAction = onAction, + ) + } } } @@ -181,12 +199,12 @@ fun MateRecruitContent( .border(1.dp, Gray008, RoundedCornerShape(8.dp)) .clip(RoundedCornerShape(8.dp)) .padding(vertical = 16.dp, horizontal = 12.dp) - .clickable {}, + .clickable { onAction(MateRecruitUiAction.OnScheduleDateClicked) }, ) { Text( - text = "2024년 08월 03일", + text = uiState.mateRecruitDate, style = Small14_SemiBold, - color = Gray007, + color = if (uiState.isMateRecruitDateUpdated) Gray001 else Gray007, ) } Spacer(modifier = Modifier.width(8.dp)) @@ -196,12 +214,12 @@ fun MateRecruitContent( .border(1.dp, Gray008, RoundedCornerShape(8.dp)) .clip(RoundedCornerShape(8.dp)) .padding(vertical = 16.dp, horizontal = 12.dp) - .clickable {}, + .clickable { onAction(MateRecruitUiAction.OnScheduleTimeClicked) }, ) { Text( - text = "오전 12시 00분", + text = uiState.mateRecruitTime, style = Small14_SemiBold, - color = Gray007, + color = if (uiState.isMateRecruitTimeUpdated) Gray001 else Gray007, ) } } @@ -218,7 +236,8 @@ fun MateRecruitContent( searchTextHintRes = R.string.mate_recruit_content_hint, modifier = Modifier .fillMaxWidth() - .height(52.dp), + .height(200.dp), + multiline = true, maxLength = 200, ) Spacer(modifier = Modifier.height(28.dp)) diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt index 880c2f18..34c0093d 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt @@ -4,6 +4,11 @@ import com.tripmate.android.domain.entity.GenderAgeGroupEntity sealed interface MateRecruitUiAction { data class OnMateRecruitTitleUpdated(val title: String) : MateRecruitUiAction + data object OnScheduleDateClicked : MateRecruitUiAction + data object OnScheduleTimeClicked : MateRecruitUiAction + data class OnScheduleDateUpdated(val date: String) : MateRecruitUiAction + data class OnScheduleTimeUpdated(val time: String) : MateRecruitUiAction + data class OnDismiss(val pickerType: PickerType) : MateRecruitUiAction data class OnMateRecruitContentUpdated(val content: String) : MateRecruitUiAction data class OnMateTypeSelected(val mateType: MateType) : MateRecruitUiAction data class OnGenderAgeGroupSelected(val group: GenderAgeGroupEntity) : MateRecruitUiAction @@ -17,6 +22,11 @@ enum class MateType { ALL, } +enum class PickerType { + DATE, + TIME, +} + enum class ErrorType { NETWORK, SERVER, diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt index aff1dd34..bb7e5888 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt @@ -1,15 +1,27 @@ package com.tripmate.android.feature.recruit.viewmodel +import com.tripmate.android.core.common.extension.formatToDate +import com.tripmate.android.core.common.extension.formatToTime import com.tripmate.android.domain.entity.GenderAgeGroupEntity import com.tripmate.android.feature.mate_recruit.R import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf +import kotlinx.datetime.Clock +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime +import java.time.LocalDate +import java.time.LocalTime +import java.time.ZoneId data class MateRecruitUiState( val mateRecruitTitle: String = "", val tripLocation: String = "서퍼비치", val tripLocationAddress: String = "강원도 양양군 현북면 하조대해안길 119", val mateRecruitContent: String = "", + val mateRecruitDate: String = LocalDate.now(ZoneId.of("Asia/Seoul")).formatToDate(), + val isMateRecruitDateUpdated: Boolean = false, + val mateRecruitTime: String = LocalTime.now(ZoneId.of("Asia/Seoul")).formatToTime(), + val isMateRecruitTimeUpdated: Boolean = false, val selectedMateType: MateType = MateType.ALL, val allGenderAgeGroups: PersistentList = persistentListOf( GenderAgeGroupEntity(id = 1, textResId = R.string.same_gender, isSelected = false), @@ -18,4 +30,6 @@ data class MateRecruitUiState( ), val selectedGenderAgeGroups: PersistentList = persistentListOf(), val openKakaoLink: String = "", + val isDatePickerVisible: Boolean = false, + val isTimePickerVisible: Boolean = false, ) diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt index ef665fa2..cd62ef07 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt @@ -1,6 +1,8 @@ package com.tripmate.android.feature.recruit.viewmodel import androidx.lifecycle.ViewModel +import com.tripmate.android.core.common.extension.formatToDate +import com.tripmate.android.core.common.extension.formatToTime import com.tripmate.android.domain.entity.GenderAgeGroupEntity import com.tripmate.android.domain.repository.MateRepository import dagger.hilt.android.lifecycle.HiltViewModel @@ -11,6 +13,8 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update +import java.time.LocalDate +import java.time.LocalTime import javax.inject.Inject @HiltViewModel @@ -27,6 +31,17 @@ class MateRecruitViewModel @Inject constructor( fun onAction(action: MateRecruitUiAction) { when (action) { is MateRecruitUiAction.OnMateRecruitTitleUpdated -> setMateRecruitTitle(action.title) + is MateRecruitUiAction.OnScheduleDateClicked -> setDatePickerVisible(true) + is MateRecruitUiAction.OnScheduleTimeClicked -> setTimePickerVisible(true) + is MateRecruitUiAction.OnScheduleDateUpdated -> setMateRecruitDate(action.date) + is MateRecruitUiAction.OnScheduleTimeUpdated -> setMateRecruitTime(action.time) + is MateRecruitUiAction.OnDismiss -> { + when (action.pickerType) { + PickerType.DATE -> setDatePickerVisible(false) + PickerType.TIME -> setTimePickerVisible(false) + } + } + is MateRecruitUiAction.OnMateTypeSelected -> setMateType(action.mateType) is MateRecruitUiAction.OnGenderAgeGroupSelected -> addGenderAgeGroup(action.group) is MateRecruitUiAction.OnGenderAgeGroupDeselected -> removeSelectedTripStyle(action.group) @@ -40,10 +55,36 @@ class MateRecruitViewModel @Inject constructor( _uiState.update { it.copy(mateRecruitTitle = title) } } + private fun setTimePickerVisible(flag: Boolean) { + _uiState.update { it.copy(isTimePickerVisible = flag) } + } + + private fun setOpenKakaoLink(link: String) { + _uiState.update { it.copy(openKakaoLink = link) } + } + private fun setMateRecruitContent(content: String) { _uiState.update { it.copy(mateRecruitContent = content) } } + private fun setMateRecruitDate(date: String) { + _uiState.update { + it.copy( + mateRecruitDate = date, + isMateRecruitDateUpdated = true, + ) + } + } + + private fun setMateRecruitTime(time: String) { + _uiState.update { + it.copy( + mateRecruitTime = time, + isMateRecruitTimeUpdated = true, + ) + } + } + private fun setMateType(mateType: MateType) { _uiState.update { it.copy(selectedMateType = mateType) } } @@ -60,7 +101,7 @@ class MateRecruitViewModel @Inject constructor( } } - private fun setOpenKakaoLink(link: String) { - _uiState.update { it.copy(openKakaoLink = link) } + private fun setDatePickerVisible(flag: Boolean) { + _uiState.update { it.copy(isDatePickerVisible = flag) } } } diff --git a/feature/mate-recruit/src/main/res/values/strings.xml b/feature/mate-recruit/src/main/res/values/strings.xml index c149dd22..ce396962 100644 --- a/feature/mate-recruit/src/main/res/values/strings.xml +++ b/feature/mate-recruit/src/main/res/values/strings.xml @@ -6,6 +6,7 @@ ex)양양 서퍼비치 동행 글쓰기 일정 + 확인 동행 모집 내용 동행 모집 내용을 작성해주세요. 동행 유형 From a48993d0cc36b384951fe2819ade15151c37cd97 Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Sun, 1 Sep 2024 04:32:27 +0900 Subject: [PATCH 14/22] [chore] style check success --- .../android/core/designsystem/component/Dialog.kt | 1 - .../core/designsystem/component/TextField.kt | 1 - detekt-config.yml | 1 + .../tripmate/android/feature/home/HomeScreen.kt | 3 +-- .../android/feature/recruit/MateRecruitScreen.kt | 7 ++++--- .../recruit/viewmodel/MateRecruitUiEvent.kt | 1 + .../recruit/viewmodel/MateRecruitUiState.kt | 3 --- .../recruit/viewmodel/MateRecruitViewModel.kt | 14 +++++++++----- .../feature/personalization/UserInfoScreen.kt | 1 - 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/Dialog.kt b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/Dialog.kt index 1968c6f1..a7968aab 100644 --- a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/Dialog.kt +++ b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/Dialog.kt @@ -23,7 +23,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.DialogProperties -import com.tripmate.android.core.designsystem.ComponentPreview import com.tripmate.android.core.designsystem.R import com.tripmate.android.core.designsystem.theme.Background02 import com.tripmate.android.core.designsystem.theme.Gray001 diff --git a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt index 42adb052..9853b10a 100644 --- a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt +++ b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt @@ -142,4 +142,3 @@ fun TripmateTextFieldPreview() { ) } } - diff --git a/detekt-config.yml b/detekt-config.yml index c2c73df4..2e8b59de 100644 --- a/detekt-config.yml +++ b/detekt-config.yml @@ -4,6 +4,7 @@ complexity: ignoreAnnotated: [ 'Composable' ] CyclomaticComplexMethod: ignoreAnnotated: [ 'Composable' ] + threshold: 20 LongParameterList: active: false TooManyFunctions: diff --git a/feature/home/src/main/kotlin/com/tripmate/android/feature/home/HomeScreen.kt b/feature/home/src/main/kotlin/com/tripmate/android/feature/home/HomeScreen.kt index ffb43b37..dd97ba86 100644 --- a/feature/home/src/main/kotlin/com/tripmate/android/feature/home/HomeScreen.kt +++ b/feature/home/src/main/kotlin/com/tripmate/android/feature/home/HomeScreen.kt @@ -7,7 +7,6 @@ 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.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -23,7 +22,7 @@ internal fun HomeRoute( ) { HomeScreen( innerPadding = innerPadding, - navigateToMateRecruit = navigateToMateRecruit, + navigateToMateRecruit = navigateToMateRecruit, ) } diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt index 298788a9..69578dbb 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt @@ -50,6 +50,7 @@ import com.tripmate.android.feature.mate_recruit.R import com.tripmate.android.feature.recruit.component.MateRecruitCheckBox import com.tripmate.android.feature.recruit.component.ScheduleBottomSheet import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiAction +import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiEvent import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiState import com.tripmate.android.feature.recruit.viewmodel.MateRecruitViewModel import com.tripmate.android.feature.recruit.viewmodel.MateType @@ -66,7 +67,8 @@ fun MateRecruitRoute( ObserveAsEvents(flow = viewModel.uiEvent) { event -> when (event) { - else -> popBackStack() + is MateRecruitUiEvent.Finish -> popBackStack() + is MateRecruitUiEvent.ShowToast -> {} } } @@ -149,8 +151,7 @@ fun MateRecruitContent( .height(60.dp) .clip(RoundedCornerShape(8.dp)) .background(Gray010), - - ) { + ) { Column( modifier = Modifier .padding(horizontal = 16.dp) diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiEvent.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiEvent.kt index 6849c7e1..112deb5a 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiEvent.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiEvent.kt @@ -3,5 +3,6 @@ package com.tripmate.android.feature.recruit.viewmodel import com.tripmate.android.core.common.UiText sealed interface MateRecruitUiEvent { + data object Finish : MateRecruitUiEvent data class ShowToast(val message: UiText) : MateRecruitUiEvent } diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt index bb7e5888..7f5a8cb9 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt @@ -6,9 +6,6 @@ import com.tripmate.android.domain.entity.GenderAgeGroupEntity import com.tripmate.android.feature.mate_recruit.R import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf -import kotlinx.datetime.Clock -import kotlinx.datetime.TimeZone -import kotlinx.datetime.toLocalDateTime import java.time.LocalDate import java.time.LocalTime import java.time.ZoneId diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt index cd62ef07..a05113fa 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt @@ -1,8 +1,7 @@ package com.tripmate.android.feature.recruit.viewmodel import androidx.lifecycle.ViewModel -import com.tripmate.android.core.common.extension.formatToDate -import com.tripmate.android.core.common.extension.formatToTime +import androidx.lifecycle.viewModelScope import com.tripmate.android.domain.entity.GenderAgeGroupEntity import com.tripmate.android.domain.repository.MateRepository import dagger.hilt.android.lifecycle.HiltViewModel @@ -13,8 +12,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update -import java.time.LocalDate -import java.time.LocalTime +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel @@ -47,7 +45,7 @@ class MateRecruitViewModel @Inject constructor( is MateRecruitUiAction.OnGenderAgeGroupDeselected -> removeSelectedTripStyle(action.group) is MateRecruitUiAction.OnMateRecruitContentUpdated -> setMateRecruitContent(action.content) is MateRecruitUiAction.OnOpenKakaoLinkUpdated -> setOpenKakaoLink(action.link) - is MateRecruitUiAction.OnDoneClick -> {} + is MateRecruitUiAction.OnDoneClick -> finish() } } @@ -104,4 +102,10 @@ class MateRecruitViewModel @Inject constructor( private fun setDatePickerVisible(flag: Boolean) { _uiState.update { it.copy(isDatePickerVisible = flag) } } + + private fun finish() { + viewModelScope.launch { + _uiEvent.send(MateRecruitUiEvent.Finish) + } + } } diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt index 8b1a1a08..41ae0819 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt @@ -38,7 +38,6 @@ import com.tripmate.android.feature.personalization.viewmodel.PersonalizationUiE import com.tripmate.android.feature.personalization.viewmodel.PersonalizationUiState import com.tripmate.android.feature.personalization.viewmodel.PersonalizationViewModel import com.tripmate.android.feature.personalization.viewmodel.ScreenType -import timber.log.Timber import kotlin.system.exitProcess @Composable From 9121d8d76e04695c28340e0bbb7d4da24eebe44a Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Sun, 1 Sep 2024 04:46:23 +0900 Subject: [PATCH 15/22] =?UTF-8?q?[chore]=20TODO=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tripmate/android/feature/personalization/UserInfoScreen.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt index 41ae0819..77009e5e 100644 --- a/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt +++ b/feature/personalization/src/main/kotlin/com/tripmate/android/feature/personalization/UserInfoScreen.kt @@ -142,6 +142,7 @@ fun UserInfoContent( color = Gray001, ) Spacer(modifier = Modifier.height(8.dp)) + // TODO 텍스트 필드가 동작하지 않는 문제 해결 TripmateTextField( text = uiState.birthDate, onTextChange = { text -> From d059f74f51174e69da6222213e01147a7e9c1d0d Mon Sep 17 00:00:00 2001 From: easyhooon Date: Sun, 1 Sep 2024 12:22:52 +0900 Subject: [PATCH 16/22] =?UTF-8?q?[feat]=20=EC=9E=91=EC=84=B1=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20=EB=B2=84=ED=8A=BC=20=ED=99=9C=EC=84=B1=ED=99=94=20?= =?UTF-8?q?validation=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tripmate/android/feature/recruit/MateRecruitScreen.kt | 8 ++++++-- .../feature/recruit/viewmodel/MateRecruitUiAction.kt | 2 +- .../feature/recruit/viewmodel/MateRecruitViewModel.kt | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt index 69578dbb..760c4b39 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt @@ -321,10 +321,14 @@ fun MateRecruitContent( ) Spacer(modifier = Modifier.height(40.dp)) TripmateButton( - onClick = {}, + onClick = { onAction(MateRecruitUiAction.OnDoneClicked) }, modifier = Modifier.fillMaxWidth(), contentPadding = PaddingValues(vertical = 18.dp), - enabled = true, + enabled = uiState.mateRecruitTitle.isNotEmpty() && + uiState.mateRecruitContent.isNotEmpty() && + uiState.selectedMateType != null && + uiState.selectedGenderAgeGroups.isNotEmpty() && + uiState.openKakaoLink.isNotEmpty(), ) { Text( text = stringResource(R.string.done), diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt index 34c0093d..87dcb0cc 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt @@ -14,7 +14,7 @@ sealed interface MateRecruitUiAction { data class OnGenderAgeGroupSelected(val group: GenderAgeGroupEntity) : MateRecruitUiAction data class OnGenderAgeGroupDeselected(val group: GenderAgeGroupEntity) : MateRecruitUiAction data class OnOpenKakaoLinkUpdated(val link: String) : MateRecruitUiAction - data object OnDoneClick : MateRecruitUiAction + data object OnDoneClicked : MateRecruitUiAction } enum class MateType { diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt index a05113fa..1a811d1b 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt @@ -45,7 +45,7 @@ class MateRecruitViewModel @Inject constructor( is MateRecruitUiAction.OnGenderAgeGroupDeselected -> removeSelectedTripStyle(action.group) is MateRecruitUiAction.OnMateRecruitContentUpdated -> setMateRecruitContent(action.content) is MateRecruitUiAction.OnOpenKakaoLinkUpdated -> setOpenKakaoLink(action.link) - is MateRecruitUiAction.OnDoneClick -> finish() + is MateRecruitUiAction.OnDoneClicked -> finish() } } From 093af24d1187e8f56b642e6fd823feb19de084c3 Mon Sep 17 00:00:00 2001 From: easyhooon Date: Sun, 1 Sep 2024 12:45:10 +0900 Subject: [PATCH 17/22] =?UTF-8?q?[fix]=20=ED=85=8D=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit maxLength 가 null 로 설정되었을 경우, 입력된 값이 무시되는 문제 해결 --- .../tripmate/android/core/designsystem/component/TextField.kt | 2 +- .../com/tripmate/android/feature/recruit/MateRecruitScreen.kt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt index 9853b10a..73e57631 100644 --- a/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt +++ b/core/designsystem/src/main/kotlin/com/tripmate/android/core/designsystem/component/TextField.kt @@ -58,7 +58,7 @@ fun TripmateTextField( BasicTextField( value = text, onValueChange = { text -> - if (maxLength != null && text.length <= maxLength) { + if (maxLength == null || text.length <= maxLength) { onTextChange(text) } }, diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt index 760c4b39..f297e806 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt @@ -326,7 +326,6 @@ fun MateRecruitContent( contentPadding = PaddingValues(vertical = 18.dp), enabled = uiState.mateRecruitTitle.isNotEmpty() && uiState.mateRecruitContent.isNotEmpty() && - uiState.selectedMateType != null && uiState.selectedGenderAgeGroups.isNotEmpty() && uiState.openKakaoLink.isNotEmpty(), ) { From 1ebe0e5b8183bc4031a95fe90286de9b61d83663 Mon Sep 17 00:00:00 2001 From: easyhooon Date: Mon, 2 Sep 2024 01:24:30 +0900 Subject: [PATCH 18/22] =?UTF-8?q?[refactor]=20wheel=20picker=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20migration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/common/build.gradle.kts | 2 + .../core/common/extension/LocalDate.kt | 7 +--- .../core/common/extension/LocalTime.kt | 15 ++++---- .../android/core/common/extension/String.kt | 38 ++++++++++++++----- feature/mate-recruit/build.gradle.kts | 8 +--- .../recruit/component/ScheduleBottomSheet.kt | 25 ++++++------ .../recruit/viewmodel/MateRecruitUiState.kt | 10 ++--- gradle/libs.versions.toml | 4 +- 8 files changed, 63 insertions(+), 46 deletions(-) diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index a15864e1..e462c1f7 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -16,6 +16,8 @@ dependencies { implementations( projects.core.domain, + libs.kotlinx.datetime, + libs.androidx.hilt.navigation.compose, libs.timber, diff --git a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalDate.kt b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalDate.kt index 37871c84..aac0d8fd 100644 --- a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalDate.kt +++ b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalDate.kt @@ -1,10 +1,7 @@ package com.tripmate.android.core.common.extension -import java.time.LocalDate -import java.time.format.DateTimeFormatter -import java.util.Locale +import kotlinx.datetime.LocalDate fun LocalDate.formatToDate(): String { - val formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일", Locale.KOREAN) - return this.format(formatter) + return "${year}년 ${monthNumber.toString().padStart(2, '0')}월 ${dayOfMonth.toString().padStart(2, '0')}일" } diff --git a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalTime.kt b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalTime.kt index d540f280..f0723361 100644 --- a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalTime.kt +++ b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalTime.kt @@ -1,12 +1,13 @@ package com.tripmate.android.core.common.extension -import java.time.LocalTime -import java.time.format.DateTimeFormatter -import java.util.Locale +import kotlinx.datetime.LocalTime fun LocalTime.formatToTime(): String { - val formatter = DateTimeFormatter.ofPattern("a h시 mm분", Locale.KOREAN) - return this.format(formatter) - .replace("AM", "오전") - .replace("PM", "오후") + val period = if (hour < 12) "오전" else "오후" + val adjustedHour = when { + hour == 0 -> 12 + hour > 12 -> hour - 12 + else -> hour + } + return "$period ${adjustedHour}시 ${minute.toString().padStart(2, '0')}분" } diff --git a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt index 01d1d11b..4de7a7f1 100644 --- a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt +++ b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt @@ -1,17 +1,37 @@ package com.tripmate.android.core.common.extension -import java.time.LocalDate -import java.time.LocalTime -import java.time.format.DateTimeFormatter -import java.util.Locale +import kotlinx.datetime.Clock +import kotlinx.datetime.LocalDate +import kotlinx.datetime.LocalTime +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime fun String.parseToLocalDate(): LocalDate { - val formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일", Locale.KOREAN) - return LocalDate.parse(this, formatter) + if (this.isBlank()) { + // 빈 문자열인 경우 현재 날짜 반환 + return Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).date + } + + val (year, month, day) = this.split("년 ", "월 ", "일") + .map { it.trim().toInt() } + return LocalDate(year, month, day) } fun String.parseToLocalTime(): LocalTime { - val formattedString = this.replace("오전", "AM").replace("오후", "PM") - val formatter = DateTimeFormatter.ofPattern("a h시 mm분", Locale.ENGLISH) - return LocalTime.parse(formattedString, formatter) + if (this.isBlank()) { + // 빈 문자열인 경우 현재 시간 반환 + return Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).time + } + + val (period, time) = this.split(" ", limit = 2) + val (hour, minute) = time.split("시 ", "분") + .map { it.trim().toInt() } + + val adjustedHour = when { + period == "오후" && hour != 12 -> hour + 12 + period == "오전" && hour == 12 -> 0 + else -> hour + } + + return LocalTime(adjustedHour, minute) } diff --git a/feature/mate-recruit/build.gradle.kts b/feature/mate-recruit/build.gradle.kts index 311d0e23..e6748f17 100644 --- a/feature/mate-recruit/build.gradle.kts +++ b/feature/mate-recruit/build.gradle.kts @@ -14,14 +14,10 @@ android { dependencies { implementations( - projects.feature.navigator, - libs.kotlinx.datetime, libs.kotlinx.collections.immutable, - libs.androidx.activity.compose, - libs.androidx.splash, - libs.compose.system.ui.controller, + libs.timber, - libs.wheel.picker.compose, + libs.datetime.wheel.picker, ) } diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleBottomSheet.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleBottomSheet.kt index 2e6e8a6d..d7605ff5 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleBottomSheet.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleBottomSheet.kt @@ -28,10 +28,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.commandiron.wheel_picker_compose.WheelDatePicker -import com.commandiron.wheel_picker_compose.WheelTimePicker -import com.commandiron.wheel_picker_compose.core.TimeFormat -import com.commandiron.wheel_picker_compose.core.WheelPickerDefaults import com.tripmate.android.core.common.extension.formatToDate import com.tripmate.android.core.common.extension.formatToTime import com.tripmate.android.core.common.extension.parseToLocalDate @@ -49,8 +45,13 @@ import com.tripmate.android.feature.mate_recruit.R import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiAction import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiState import com.tripmate.android.feature.recruit.viewmodel.PickerType -import java.time.LocalDate -import java.time.LocalTime +import dev.darkokoa.datetimewheelpicker.WheelDatePicker +import dev.darkokoa.datetimewheelpicker.WheelTimePicker +import dev.darkokoa.datetimewheelpicker.core.TimeFormat +import dev.darkokoa.datetimewheelpicker.core.WheelPickerDefaults +import kotlinx.datetime.Clock +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -60,8 +61,8 @@ fun ScheduleBottomSheet( onAction: (MateRecruitUiAction) -> Unit, ) { val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) - var selectedDate by remember { mutableStateOf("") } - var selectedTime by remember { mutableStateOf("") } + var selectedDate by remember { mutableStateOf(uiState.mateRecruitDate) } + var selectedTime by remember { mutableStateOf(uiState.mateRecruitTime) } ModalBottomSheet( onDismissRequest = { @@ -100,8 +101,8 @@ fun ScheduleBottomSheet( .fillMaxWidth() .padding(horizontal = 15.dp), startDate = uiState.mateRecruitDate.parseToLocalDate(), - minDate = LocalDate.now(), - maxDate = LocalDate.of(2030, 12, 31), + minDate = Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).date, + maxDate = kotlinx.datetime.LocalDate(2030, 12, 31), yearsRange = IntRange(2024, 2030), rowCount = 5, textStyle = Large20_Mid, @@ -119,8 +120,8 @@ fun ScheduleBottomSheet( .fillMaxWidth() .padding(horizontal = 15.dp), startTime = uiState.mateRecruitTime.parseToLocalTime(), - minTime = LocalTime.now(), - maxTime = LocalTime.of(23, 59), + minTime = Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).time, + maxTime = kotlinx.datetime.LocalTime(23, 59), timeFormat = TimeFormat.AM_PM, rowCount = 5, textStyle = Large20_Mid, diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt index 7f5a8cb9..e5970354 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt @@ -6,18 +6,18 @@ import com.tripmate.android.domain.entity.GenderAgeGroupEntity import com.tripmate.android.feature.mate_recruit.R import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf -import java.time.LocalDate -import java.time.LocalTime -import java.time.ZoneId +import kotlinx.datetime.Clock +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime data class MateRecruitUiState( val mateRecruitTitle: String = "", val tripLocation: String = "서퍼비치", val tripLocationAddress: String = "강원도 양양군 현북면 하조대해안길 119", val mateRecruitContent: String = "", - val mateRecruitDate: String = LocalDate.now(ZoneId.of("Asia/Seoul")).formatToDate(), + val mateRecruitDate: String = Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).date.formatToDate(), val isMateRecruitDateUpdated: Boolean = false, - val mateRecruitTime: String = LocalTime.now(ZoneId.of("Asia/Seoul")).formatToTime(), + val mateRecruitTime: String = Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).time.formatToTime(), val isMateRecruitTimeUpdated: Boolean = false, val selectedMateType: MateType = MateType.ALL, val allGenderAgeGroups: PersistentList = persistentListOf( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 82e1949e..f4f54fa8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -56,7 +56,7 @@ compose-stable-marker = "1.0.4" landscapist = "2.3.3" kakao-maps = "2.9.5" kakao-core = "2.20.4" -wheel-picker-compose = "1.1.11" +datetime-wheel-picker = "1.0.0" [libraries] @@ -117,7 +117,7 @@ landscapist-coil = { group = "com.github.skydoves", name = "landscapist-coil" } landscapist-placeholder = { group = "com.github.skydoves", name = "landscapist-placeholder" } kakao-maps = { group = "com.kakao.maps.open", name = "android", version.ref = "kakao-maps" } kakao-auth = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakao-core" } -wheel-picker-compose = { group = "com.github.commandiron", name = "WheelPickerCompose", version.ref = "wheel-picker-compose" } +datetime-wheel-picker = { group = "io.github.darkokoa", name = "datetime-wheel-picker", version.ref = "datetime-wheel-picker" } [plugins] From 14e3083e87f83a45059bab9a43f103744fa8c69b Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Mon, 2 Sep 2024 12:59:16 +0900 Subject: [PATCH 19/22] =?UTF-8?q?[fix]=20LocalDate,=20Time=20=ED=8C=8C?= =?UTF-8?q?=EC=8B=B1=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tripmate/android/core/common/extension/String.kt | 11 +++++++++-- .../feature/recruit/component/ScheduleBottomSheet.kt | 4 +++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt index 4de7a7f1..db1125f5 100644 --- a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt +++ b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt @@ -8,11 +8,13 @@ import kotlinx.datetime.toLocalDateTime fun String.parseToLocalDate(): LocalDate { if (this.isBlank()) { - // 빈 문자열인 경우 현재 날짜 반환 return Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).date } - val (year, month, day) = this.split("년 ", "월 ", "일") + require(this.matches(Regex("^\\d{4}년\\s*\\d{1,2}월\\s*\\d{1,2}일$"))) { "잘못된 날짜 형식: $this" } + + val (year, month, day) = this.split("년", "월", "일") + .filter { it.isNotBlank() } .map { it.trim().toInt() } return LocalDate(year, month, day) } @@ -23,10 +25,15 @@ fun String.parseToLocalTime(): LocalTime { return Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).time } + require(this.matches(Regex("^(오전|오후)\\s*\\d{1,2}시\\s*\\d{1,2}분$"))) { "잘못된 시간 형식: $this" } + val (period, time) = this.split(" ", limit = 2) val (hour, minute) = time.split("시 ", "분") + .filter { it.isNotBlank() } .map { it.trim().toInt() } + require(hour in 1..12 && minute in 0..59) { "유효하지 않은 시간 또는 분: $this" } + val adjustedHour = when { period == "오후" && hour != 12 -> hour + 12 period == "오전" && hour == 12 -> 0 diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleBottomSheet.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleBottomSheet.kt index d7605ff5..bb16307e 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleBottomSheet.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleBottomSheet.kt @@ -92,7 +92,9 @@ fun ScheduleBottomSheet( modifier = Modifier.wrapContentHeight(), ) { Column( - modifier = Modifier.navigationBarsPadding(), + modifier = Modifier + .background(Background02) + .navigationBarsPadding(), horizontalAlignment = Alignment.CenterHorizontally, ) { if (pickerType == PickerType.DATE) { From ff6e7c916af2294326a90a250b491e3023c8bf32 Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Mon, 2 Sep 2024 14:02:26 +0900 Subject: [PATCH 20/22] =?UTF-8?q?[fix]=20DateTimePicker=20Dialog=20?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/recruit/MateRecruitScreen.kt | 8 +- ...ttomSheet.kt => ScheduleDateTimePicker.kt} | 111 +++++++++++++++++- 2 files changed, 114 insertions(+), 5 deletions(-) rename feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/{ScheduleBottomSheet.kt => ScheduleDateTimePicker.kt} (63%) diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt index f297e806..5af41369 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt @@ -48,7 +48,7 @@ import com.tripmate.android.core.designsystem.theme.XSmall12_Reg import com.tripmate.android.core.ui.DevicePreview import com.tripmate.android.feature.mate_recruit.R import com.tripmate.android.feature.recruit.component.MateRecruitCheckBox -import com.tripmate.android.feature.recruit.component.ScheduleBottomSheet +import com.tripmate.android.feature.recruit.component.ScheduleDialog import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiAction import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiEvent import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiState @@ -102,18 +102,20 @@ fun MateRecruitScreen( } if (uiState.isDatePickerVisible) { - ScheduleBottomSheet( + ScheduleDialog( pickerType = PickerType.DATE, uiState = uiState, onAction = onAction, + onDismissRequest = {}, ) } if (uiState.isTimePickerVisible) { - ScheduleBottomSheet( + ScheduleDialog( pickerType = PickerType.TIME, uiState = uiState, onAction = onAction, + onDismissRequest = {}, ) } } diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleBottomSheet.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleDateTimePicker.kt similarity index 63% rename from feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleBottomSheet.kt rename to feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleDateTimePicker.kt index bb16307e..205af976 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleBottomSheet.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleDateTimePicker.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.ModalBottomSheet @@ -28,6 +29,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.DialogProperties import com.tripmate.android.core.common.extension.formatToDate import com.tripmate.android.core.common.extension.formatToTime import com.tripmate.android.core.common.extension.parseToLocalDate @@ -50,6 +52,7 @@ import dev.darkokoa.datetimewheelpicker.WheelTimePicker import dev.darkokoa.datetimewheelpicker.core.TimeFormat import dev.darkokoa.datetimewheelpicker.core.WheelPickerDefaults import kotlinx.datetime.Clock +import kotlinx.datetime.LocalDate import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime @@ -104,7 +107,7 @@ fun ScheduleBottomSheet( .padding(horizontal = 15.dp), startDate = uiState.mateRecruitDate.parseToLocalDate(), minDate = Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).date, - maxDate = kotlinx.datetime.LocalDate(2030, 12, 31), + maxDate = LocalDate(2030, 12, 31), yearsRange = IntRange(2024, 2030), rowCount = 5, textStyle = Large20_Mid, @@ -162,9 +165,100 @@ fun ScheduleBottomSheet( } } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ScheduleDialog( + pickerType: PickerType, + uiState: MateRecruitUiState, + onAction: (MateRecruitUiAction) -> Unit, + onDismissRequest: () -> Unit, + modifier: Modifier = Modifier, + properties: DialogProperties = DialogProperties(), +) { + var selectedDate by remember { mutableStateOf(uiState.mateRecruitDate) } + var selectedTime by remember { mutableStateOf(uiState.mateRecruitTime) } + + BasicAlertDialog( + onDismissRequest = onDismissRequest, + modifier = modifier, + properties = properties, + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(16.dp)) + .background(color = Background02), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Spacer(modifier = Modifier.height(24.dp)) + if (pickerType == PickerType.DATE) { + WheelDatePicker( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 15.dp), + startDate = uiState.mateRecruitDate.parseToLocalDate(), + minDate = Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).date, + maxDate = LocalDate(2030, 12, 31), + yearsRange = IntRange(2024, 2030), + rowCount = 5, + textStyle = Large20_Mid, + textColor = Gray001, + selectorProperties = WheelPickerDefaults.selectorProperties( + enabled = true, + shape = RoundedCornerShape(10.dp), + color = Gray010, + border = BorderStroke(2.dp, Gray010), + ), + ) { snappedDate -> run { selectedDate = snappedDate.formatToDate() } } + } else { + WheelTimePicker( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 15.dp), + startTime = uiState.mateRecruitTime.parseToLocalTime(), + minTime = Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).time, + maxTime = kotlinx.datetime.LocalTime(23, 59), + timeFormat = TimeFormat.AM_PM, + rowCount = 5, + textStyle = Large20_Mid, + textColor = Gray001, + selectorProperties = WheelPickerDefaults.selectorProperties( + enabled = true, + shape = RoundedCornerShape(10.dp), + color = Gray010, + border = BorderStroke(2.dp, Gray010), + ), + ) { snappedTime -> run { selectedTime = snappedTime.formatToTime() } } + } + Spacer(modifier = Modifier.height(24.dp)) + TripmateButton( + onClick = { + if (pickerType == PickerType.DATE) { + onAction(MateRecruitUiAction.OnScheduleDateUpdated(selectedDate)) + } else { + onAction(MateRecruitUiAction.OnScheduleTimeUpdated(selectedTime)) + } + onAction(MateRecruitUiAction.OnDismiss(pickerType)) + }, + modifier = Modifier + .fillMaxWidth() + .height(56.dp) + .padding(horizontal = 15.dp), + ) { + Text( + text = stringResource(id = R.string.confirm), + style = Medium16_SemiBold, + color = Color.White, + ) + } + Spacer(modifier = Modifier.height(24.dp)) + } + } +} + @ComponentPreview @Composable -fun SchoolSearchBottomSheetPreview() { +fun ScheduleBottomSheetPreview() { TripmateTheme { ScheduleBottomSheet( pickerType = PickerType.DATE, @@ -173,3 +267,16 @@ fun SchoolSearchBottomSheetPreview() { ) } } + +@ComponentPreview +@Composable +fun ScheduleDialogPreview() { + TripmateTheme { + ScheduleDialog( + pickerType = PickerType.DATE, + uiState = MateRecruitUiState(), + onAction = {}, + onDismissRequest = {}, + ) + } +} From 01e192c0deeaa2da5b4f953cc9a9debd1f0353d7 Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Mon, 2 Sep 2024 14:35:07 +0900 Subject: [PATCH 21/22] =?UTF-8?q?[fix]=20Wheel=20Picker=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EB=A1=A4=EB=B0=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Locale 지원을 위함 --- .../core/common/extension/LocalDate.kt | 11 ++- .../core/common/extension/LocalTime.kt | 25 ++++-- .../android/core/common/extension/String.kt | 82 +++++++++++-------- feature/mate-recruit/build.gradle.kts | 2 +- .../component/ScheduleDateTimePicker.kt | 31 ++++--- .../recruit/viewmodel/MateRecruitUiState.kt | 10 +-- gradle/libs.versions.toml | 2 + 7 files changed, 94 insertions(+), 69 deletions(-) diff --git a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalDate.kt b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalDate.kt index aac0d8fd..0a946967 100644 --- a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalDate.kt +++ b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalDate.kt @@ -1,7 +1,14 @@ package com.tripmate.android.core.common.extension -import kotlinx.datetime.LocalDate +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import java.util.Locale fun LocalDate.formatToDate(): String { - return "${year}년 ${monthNumber.toString().padStart(2, '0')}월 ${dayOfMonth.toString().padStart(2, '0')}일" + val formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일", Locale.KOREAN) + return this.format(formatter) } + +// fun LocalDate.formatToDate(): String { +// return "${year}년 ${monthNumber.toString().padStart(2, '0')}월 ${dayOfMonth.toString().padStart(2, '0')}일" +// } diff --git a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalTime.kt b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalTime.kt index f0723361..7a880146 100644 --- a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalTime.kt +++ b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/LocalTime.kt @@ -1,13 +1,22 @@ package com.tripmate.android.core.common.extension -import kotlinx.datetime.LocalTime +import java.time.LocalTime +import java.time.format.DateTimeFormatter +import java.util.Locale fun LocalTime.formatToTime(): String { - val period = if (hour < 12) "오전" else "오후" - val adjustedHour = when { - hour == 0 -> 12 - hour > 12 -> hour - 12 - else -> hour - } - return "$period ${adjustedHour}시 ${minute.toString().padStart(2, '0')}분" + val formatter = DateTimeFormatter.ofPattern("a h시 mm분", Locale.KOREAN) + return this.format(formatter) + .replace("AM", "오전") + .replace("PM", "오후") } + +// fun LocalTime.formatToTime(): String { +// val period = if (hour < 12) "오전" else "오후" +// val adjustedHour = when { +// hour == 0 -> 12 +// hour > 12 -> hour - 12 +// else -> hour +// } +// return "$period ${adjustedHour}시 ${minute.toString().padStart(2, '0')}분" +// } diff --git a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt index db1125f5..2d90c0e4 100644 --- a/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt +++ b/core/common/src/main/kotlin/com/tripmate/android/core/common/extension/String.kt @@ -1,44 +1,54 @@ package com.tripmate.android.core.common.extension -import kotlinx.datetime.Clock -import kotlinx.datetime.LocalDate -import kotlinx.datetime.LocalTime -import kotlinx.datetime.TimeZone -import kotlinx.datetime.toLocalDateTime +import java.time.LocalDate +import java.time.LocalTime +import java.time.format.DateTimeFormatter +import java.util.Locale fun String.parseToLocalDate(): LocalDate { - if (this.isBlank()) { - return Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).date - } - - require(this.matches(Regex("^\\d{4}년\\s*\\d{1,2}월\\s*\\d{1,2}일$"))) { "잘못된 날짜 형식: $this" } - - val (year, month, day) = this.split("년", "월", "일") - .filter { it.isNotBlank() } - .map { it.trim().toInt() } - return LocalDate(year, month, day) + val formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일", Locale.KOREAN) + return LocalDate.parse(this, formatter) } fun String.parseToLocalTime(): LocalTime { - if (this.isBlank()) { - // 빈 문자열인 경우 현재 시간 반환 - return Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).time - } - - require(this.matches(Regex("^(오전|오후)\\s*\\d{1,2}시\\s*\\d{1,2}분$"))) { "잘못된 시간 형식: $this" } - - val (period, time) = this.split(" ", limit = 2) - val (hour, minute) = time.split("시 ", "분") - .filter { it.isNotBlank() } - .map { it.trim().toInt() } - - require(hour in 1..12 && minute in 0..59) { "유효하지 않은 시간 또는 분: $this" } - - val adjustedHour = when { - period == "오후" && hour != 12 -> hour + 12 - period == "오전" && hour == 12 -> 0 - else -> hour - } - - return LocalTime(adjustedHour, minute) + val formattedString = this.replace("오전", "AM").replace("오후", "PM") + val formatter = DateTimeFormatter.ofPattern("a h시 mm분", Locale.ENGLISH) + return LocalTime.parse(formattedString, formatter) } + +// fun String.parseToLocalDate(): LocalDate { +// if (this.isBlank()) { +// return Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).date +// } +// +// require(this.matches(Regex("^\\d{4}년\\s*\\d{1,2}월\\s*\\d{1,2}일$"))) { "잘못된 날짜 형식: $this" } +// +// val (year, month, day) = this.split("년", "월", "일") +// .filter { it.isNotBlank() } +// .map { it.trim().toInt() } +// return LocalDate(year, month, day) +// } +// +// fun String.parseToLocalTime(): LocalTime { +// if (this.isBlank()) { +// // 빈 문자열인 경우 현재 시간 반환 +// return Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).time +// } +// +// require(this.matches(Regex("^(오전|오후)\\s*\\d{1,2}시\\s*\\d{1,2}분$"))) { "잘못된 시간 형식: $this" } +// +// val (period, time) = this.split(" ", limit = 2) +// val (hour, minute) = time.split("시 ", "분") +// .filter { it.isNotBlank() } +// .map { it.trim().toInt() } +// +// require(hour in 1..12 && minute in 0..59) { "유효하지 않은 시간 또는 분: $this" } +// +// val adjustedHour = when { +// period == "오후" && hour != 12 -> hour + 12 +// period == "오전" && hour == 12 -> 0 +// else -> hour +// } +// +// return LocalTime(adjustedHour, minute) +// } diff --git a/feature/mate-recruit/build.gradle.kts b/feature/mate-recruit/build.gradle.kts index e6748f17..3d334d13 100644 --- a/feature/mate-recruit/build.gradle.kts +++ b/feature/mate-recruit/build.gradle.kts @@ -18,6 +18,6 @@ dependencies { libs.kotlinx.collections.immutable, libs.timber, - libs.datetime.wheel.picker, + libs.wheel.picker.compose, ) } diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleDateTimePicker.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleDateTimePicker.kt index 205af976..6c86c5c4 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleDateTimePicker.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/component/ScheduleDateTimePicker.kt @@ -30,6 +30,10 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.DialogProperties +import com.commandiron.wheel_picker_compose.WheelDatePicker +import com.commandiron.wheel_picker_compose.WheelTimePicker +import com.commandiron.wheel_picker_compose.core.TimeFormat +import com.commandiron.wheel_picker_compose.core.WheelPickerDefaults import com.tripmate.android.core.common.extension.formatToDate import com.tripmate.android.core.common.extension.formatToTime import com.tripmate.android.core.common.extension.parseToLocalDate @@ -47,14 +51,8 @@ import com.tripmate.android.feature.mate_recruit.R import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiAction import com.tripmate.android.feature.recruit.viewmodel.MateRecruitUiState import com.tripmate.android.feature.recruit.viewmodel.PickerType -import dev.darkokoa.datetimewheelpicker.WheelDatePicker -import dev.darkokoa.datetimewheelpicker.WheelTimePicker -import dev.darkokoa.datetimewheelpicker.core.TimeFormat -import dev.darkokoa.datetimewheelpicker.core.WheelPickerDefaults -import kotlinx.datetime.Clock -import kotlinx.datetime.LocalDate -import kotlinx.datetime.TimeZone -import kotlinx.datetime.toLocalDateTime +import java.time.LocalDate +import java.time.LocalTime @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -106,8 +104,8 @@ fun ScheduleBottomSheet( .fillMaxWidth() .padding(horizontal = 15.dp), startDate = uiState.mateRecruitDate.parseToLocalDate(), - minDate = Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).date, - maxDate = LocalDate(2030, 12, 31), + minDate = LocalDate.now(), + maxDate = LocalDate.of(2030, 12, 31), yearsRange = IntRange(2024, 2030), rowCount = 5, textStyle = Large20_Mid, @@ -125,8 +123,8 @@ fun ScheduleBottomSheet( .fillMaxWidth() .padding(horizontal = 15.dp), startTime = uiState.mateRecruitTime.parseToLocalTime(), - minTime = Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).time, - maxTime = kotlinx.datetime.LocalTime(23, 59), + minTime = LocalTime.now(), + maxTime = LocalTime.of(23, 59), timeFormat = TimeFormat.AM_PM, rowCount = 5, textStyle = Large20_Mid, @@ -197,9 +195,8 @@ fun ScheduleDialog( .fillMaxWidth() .padding(horizontal = 15.dp), startDate = uiState.mateRecruitDate.parseToLocalDate(), - minDate = Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).date, - maxDate = LocalDate(2030, 12, 31), - yearsRange = IntRange(2024, 2030), + minDate = LocalDate.now(), + maxDate = LocalDate.of(2030, 12, 31), rowCount = 5, textStyle = Large20_Mid, textColor = Gray001, @@ -216,8 +213,8 @@ fun ScheduleDialog( .fillMaxWidth() .padding(horizontal = 15.dp), startTime = uiState.mateRecruitTime.parseToLocalTime(), - minTime = Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).time, - maxTime = kotlinx.datetime.LocalTime(23, 59), + minTime = LocalTime.now(), + maxTime = LocalTime.of(23, 59), timeFormat = TimeFormat.AM_PM, rowCount = 5, textStyle = Large20_Mid, diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt index e5970354..7f5a8cb9 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiState.kt @@ -6,18 +6,18 @@ import com.tripmate.android.domain.entity.GenderAgeGroupEntity import com.tripmate.android.feature.mate_recruit.R import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf -import kotlinx.datetime.Clock -import kotlinx.datetime.TimeZone -import kotlinx.datetime.toLocalDateTime +import java.time.LocalDate +import java.time.LocalTime +import java.time.ZoneId data class MateRecruitUiState( val mateRecruitTitle: String = "", val tripLocation: String = "서퍼비치", val tripLocationAddress: String = "강원도 양양군 현북면 하조대해안길 119", val mateRecruitContent: String = "", - val mateRecruitDate: String = Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).date.formatToDate(), + val mateRecruitDate: String = LocalDate.now(ZoneId.of("Asia/Seoul")).formatToDate(), val isMateRecruitDateUpdated: Boolean = false, - val mateRecruitTime: String = Clock.System.now().toLocalDateTime(TimeZone.of("Asia/Seoul")).time.formatToTime(), + val mateRecruitTime: String = LocalTime.now(ZoneId.of("Asia/Seoul")).formatToTime(), val isMateRecruitTimeUpdated: Boolean = false, val selectedMateType: MateType = MateType.ALL, val allGenderAgeGroups: PersistentList = persistentListOf( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f4f54fa8..af5c6ce0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -56,6 +56,7 @@ compose-stable-marker = "1.0.4" landscapist = "2.3.3" kakao-maps = "2.9.5" kakao-core = "2.20.4" +wheel-picker-compose = "1.1.11" datetime-wheel-picker = "1.0.0" [libraries] @@ -118,6 +119,7 @@ landscapist-placeholder = { group = "com.github.skydoves", name = "landscapist-p kakao-maps = { group = "com.kakao.maps.open", name = "android", version.ref = "kakao-maps" } kakao-auth = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakao-core" } datetime-wheel-picker = { group = "io.github.darkokoa", name = "datetime-wheel-picker", version.ref = "datetime-wheel-picker" } +wheel-picker-compose = { group = "com.github.commandiron", name = "WheelPickerCompose", version.ref = "wheel-picker-compose" } [plugins] From f37b1930723b9df10c012b71ec0217ffca1e366f Mon Sep 17 00:00:00 2001 From: "jihun.lee" Date: Mon, 2 Sep 2024 15:47:53 +0900 Subject: [PATCH 22/22] =?UTF-8?q?[feat]=20=ED=83=AD=EB=B0=94=20=EB=92=A4?= =?UTF-8?q?=EB=A1=9C=EA=B0=80=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tripmate/android/feature/recruit/MateRecruitScreen.kt | 2 ++ .../feature/recruit/viewmodel/MateRecruitUiAction.kt | 1 + .../feature/recruit/viewmodel/MateRecruitUiEvent.kt | 1 + .../feature/recruit/viewmodel/MateRecruitViewModel.kt | 7 +++++++ 4 files changed, 11 insertions(+) diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt index 5af41369..06dbde48 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/MateRecruitScreen.kt @@ -67,6 +67,7 @@ fun MateRecruitRoute( ObserveAsEvents(flow = viewModel.uiEvent) { event -> when (event) { + is MateRecruitUiEvent.NavigateBack -> popBackStack() is MateRecruitUiEvent.Finish -> popBackStack() is MateRecruitUiEvent.ShowToast -> {} } @@ -94,6 +95,7 @@ fun MateRecruitScreen( TripmateTopAppBar( navigationType = TopAppBarNavigationType.Back, title = stringResource(id = R.string.mate_writing), + onNavigationClick = { onAction(MateRecruitUiAction.OnBackClicked) }, ) MateRecruitContent( uiState = uiState, diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt index 87dcb0cc..6f370e3c 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiAction.kt @@ -3,6 +3,7 @@ package com.tripmate.android.feature.recruit.viewmodel import com.tripmate.android.domain.entity.GenderAgeGroupEntity sealed interface MateRecruitUiAction { + data object OnBackClicked : MateRecruitUiAction data class OnMateRecruitTitleUpdated(val title: String) : MateRecruitUiAction data object OnScheduleDateClicked : MateRecruitUiAction data object OnScheduleTimeClicked : MateRecruitUiAction diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiEvent.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiEvent.kt index 112deb5a..f2a46985 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiEvent.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitUiEvent.kt @@ -3,6 +3,7 @@ package com.tripmate.android.feature.recruit.viewmodel import com.tripmate.android.core.common.UiText sealed interface MateRecruitUiEvent { + data object NavigateBack : MateRecruitUiEvent data object Finish : MateRecruitUiEvent data class ShowToast(val message: UiText) : MateRecruitUiEvent } diff --git a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt index 1a811d1b..ece17e85 100644 --- a/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt +++ b/feature/mate-recruit/src/main/kotlin/com/tripmate/android/feature/recruit/viewmodel/MateRecruitViewModel.kt @@ -28,6 +28,7 @@ class MateRecruitViewModel @Inject constructor( fun onAction(action: MateRecruitUiAction) { when (action) { + is MateRecruitUiAction.OnBackClicked -> navigateBack() is MateRecruitUiAction.OnMateRecruitTitleUpdated -> setMateRecruitTitle(action.title) is MateRecruitUiAction.OnScheduleDateClicked -> setDatePickerVisible(true) is MateRecruitUiAction.OnScheduleTimeClicked -> setTimePickerVisible(true) @@ -49,6 +50,12 @@ class MateRecruitViewModel @Inject constructor( } } + private fun navigateBack() { + viewModelScope.launch { + _uiEvent.send(MateRecruitUiEvent.NavigateBack) + } + } + private fun setMateRecruitTitle(title: String) { _uiState.update { it.copy(mateRecruitTitle = title) } }