From 2511d2178277a91f8ba35cc5b6e7f6141c62d957 Mon Sep 17 00:00:00 2001 From: GunHyung Ham <54674781+ham2174@users.noreply.github.com> Date: Tue, 20 Feb 2024 22:40:43 +0900 Subject: [PATCH] =?UTF-8?q?[Feat]=20=EC=98=A8=EB=B3=B4=EB=94=A9=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EC=97=B0=EA=B2=B0=20=EB=B0=8F=20UX=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [fix] : 지하철 텍스트필드 입력시 자동으로 최하단으로 스크롤되게 수정 * [refactor] : 지하철 텍스트필드 포커스시 스크롤 최하단으로 가게 수정 * [fix] : 모든 아이콘 선택시 포커스 clear로 변경 * [feat] : 온보딩-프로필생성 네비게이션 연결 * [feat] : 시스템 네비게이션바 컬러 변경 * [feat] : 프로필 존재시 홈 화면으로 진입 * [refactor] : 불필요한 주석 제거 --- app/build.gradle.kts | 1 + .../main/java/com/moya/funch/MainActivity.kt | 8 +- .../com/moya/funch/navigation/FunchNavHost.kt | 9 +- .../main/java/com/moya/funch/ui/FunchApp.kt | 5 +- .../java/com/moya/funch/component/Chip.kt | 1 - .../main/java/com/moya/funch/theme/Theme.kt | 1 + .../moya/funch/navigation/HomeNavigation.kt | 2 +- .../navigation/OnBoardingNavigation.kt | 4 +- .../com/moya/funch/CreatePofileViewModel.kt | 2 +- .../com/moya/funch/CreateProflieScreen.kt | 139 ++++++++++++------ .../funch/navigation/MyProfileNavigatoin.kt | 5 +- 11 files changed, 120 insertions(+), 57 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c8957502..54efdc68 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -35,6 +35,7 @@ dependencies { implementation(projects.core.designsystem) implementation(projects.core.domain) implementation(projects.core.data) + implementation(projects.core.datastore) // feature implementation(projects.feature.profile) implementation(projects.feature.home) diff --git a/app/src/main/java/com/moya/funch/MainActivity.kt b/app/src/main/java/com/moya/funch/MainActivity.kt index 1da044e7..7867ebe2 100644 --- a/app/src/main/java/com/moya/funch/MainActivity.kt +++ b/app/src/main/java/com/moya/funch/MainActivity.kt @@ -9,14 +9,20 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import com.moya.funch.datastore.UserDataStore import com.moya.funch.splash.LoadingScreen import com.moya.funch.theme.FunchTheme import com.moya.funch.ui.FunchApp import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import kotlinx.coroutines.delay @AndroidEntryPoint class MainActivity : ComponentActivity() { + + @Inject + lateinit var dataStore: UserDataStore + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) installSplashScreen() @@ -32,7 +38,7 @@ class MainActivity : ComponentActivity() { if (showLoading) { LoadingScreen() } else { - FunchApp() + FunchApp(dataStore = dataStore) } } } diff --git a/app/src/main/java/com/moya/funch/navigation/FunchNavHost.kt b/app/src/main/java/com/moya/funch/navigation/FunchNavHost.kt index 31eb429e..0e0355ba 100644 --- a/app/src/main/java/com/moya/funch/navigation/FunchNavHost.kt +++ b/app/src/main/java/com/moya/funch/navigation/FunchNavHost.kt @@ -8,6 +8,8 @@ import androidx.navigation.compose.rememberNavController import androidx.navigation.navOptions import com.moya.funch.match.navigation.matchingScreen import com.moya.funch.match.navigation.navigateToMatching +import com.moya.funch.onboarding.navigation.ON_BOARDING_ROUTE +import com.moya.funch.onboarding.navigation.onBoardingScreen @Composable fun FunchNavHost(hasProfile: Boolean, navController: NavHostController = rememberNavController()) { @@ -17,14 +19,15 @@ fun FunchNavHost(hasProfile: Boolean, navController: NavHostController = remembe ) { with(navController) { profileGraph( - onNavigateToHome = ::navigateToHome, - onCloseMyProfile = ::closeMyProfile + onNavigateToHome = ::onNavigateToHome, + onCloseMyProfile = ::onCloseMyProfile ) homeScreen( onNavigateToMatching = ::onNavigateToMatching, onNavigateToMyProfile = ::onNavigateToMyProfile ) matchingScreen(onClose = { popBackStack(HOME_ROUTE, false) }) + onBoardingScreen(onNavigateToCreateProfile = ::navigateToCreateProfile) } } } @@ -39,5 +42,5 @@ private val singleTopNavOptions = navOptions { } private fun determineStartDestination(hasProfile: Boolean): String { - return if (hasProfile) HOME_ROUTE else PROFILE_GRAPH_ROUTE + return if (hasProfile) HOME_ROUTE else ON_BOARDING_ROUTE } diff --git a/app/src/main/java/com/moya/funch/ui/FunchApp.kt b/app/src/main/java/com/moya/funch/ui/FunchApp.kt index 171440f7..c6bc4f95 100644 --- a/app/src/main/java/com/moya/funch/ui/FunchApp.kt +++ b/app/src/main/java/com/moya/funch/ui/FunchApp.kt @@ -4,11 +4,12 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import com.moya.funch.datastore.UserDataStore import com.moya.funch.navigation.FunchNavHost import com.moya.funch.theme.LocalBackgroundTheme @Composable -fun FunchApp() { +fun FunchApp(dataStore: UserDataStore) { val backgroundColor = LocalBackgroundTheme.current.color Surface( @@ -16,7 +17,7 @@ fun FunchApp() { color = backgroundColor ) { FunchNavHost( - hasProfile = false + hasProfile = dataStore.hasUserId() ) } } diff --git a/core/designsystem/src/main/java/com/moya/funch/component/Chip.kt b/core/designsystem/src/main/java/com/moya/funch/component/Chip.kt index efdd21fd..0564adbd 100644 --- a/core/designsystem/src/main/java/com/moya/funch/component/Chip.kt +++ b/core/designsystem/src/main/java/com/moya/funch/component/Chip.kt @@ -166,7 +166,6 @@ data class PunchChipColors( private val selectedLabelColor: Color = White, private val disabledLabelColor: Color = Gray400, private val disabledSelectedLabelColor: Color = White - // private val ) { @Stable fun provideContainerColor(enabled: Boolean, selected: Boolean): Color { diff --git a/core/designsystem/src/main/java/com/moya/funch/theme/Theme.kt b/core/designsystem/src/main/java/com/moya/funch/theme/Theme.kt index 165c842f..7a5c3dc6 100644 --- a/core/designsystem/src/main/java/com/moya/funch/theme/Theme.kt +++ b/core/designsystem/src/main/java/com/moya/funch/theme/Theme.kt @@ -35,6 +35,7 @@ private fun statusBar() { SideEffect { val window = (view.context as Activity).window window.statusBarColor = Gray900.toArgb() + window.navigationBarColor = Gray900.toArgb() } } } diff --git a/feature/home/src/main/java/com/moya/funch/navigation/HomeNavigation.kt b/feature/home/src/main/java/com/moya/funch/navigation/HomeNavigation.kt index 250a019e..752a8c2c 100644 --- a/feature/home/src/main/java/com/moya/funch/navigation/HomeNavigation.kt +++ b/feature/home/src/main/java/com/moya/funch/navigation/HomeNavigation.kt @@ -7,7 +7,7 @@ import com.moya.funch.HomeRoute const val HOME_ROUTE = "home" -fun NavController.navigateToHome() = navigate(HOME_ROUTE) { +fun NavController.onNavigateToHome() = navigate(HOME_ROUTE) { popUpTo(graph.id) } diff --git a/feature/onboarding/src/main/java/com/moya/funch/onboarding/navigation/OnBoardingNavigation.kt b/feature/onboarding/src/main/java/com/moya/funch/onboarding/navigation/OnBoardingNavigation.kt index 5fbb12ff..48ccd658 100644 --- a/feature/onboarding/src/main/java/com/moya/funch/onboarding/navigation/OnBoardingNavigation.kt +++ b/feature/onboarding/src/main/java/com/moya/funch/onboarding/navigation/OnBoardingNavigation.kt @@ -4,10 +4,10 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.moya.funch.onboarding.OnBoardingScreen -const val ON_BOARDING_ROUTE = "home" +const val ON_BOARDING_ROUTE = "on_boarding" fun NavGraphBuilder.onBoardingScreen(onNavigateToCreateProfile: () -> Unit) { composable(route = ON_BOARDING_ROUTE) { - OnBoardingScreen(onNavigateToCreateProfile) + OnBoardingScreen(onNavigateToCreateProfile = onNavigateToCreateProfile) } } diff --git a/feature/profile/src/main/java/com/moya/funch/CreatePofileViewModel.kt b/feature/profile/src/main/java/com/moya/funch/CreatePofileViewModel.kt index aa9689ab..aff0f376 100644 --- a/feature/profile/src/main/java/com/moya/funch/CreatePofileViewModel.kt +++ b/feature/profile/src/main/java/com/moya/funch/CreatePofileViewModel.kt @@ -113,7 +113,7 @@ internal class CreateProfileViewModel @Inject constructor( onSuccess = { response -> val newState = when { response.isEmpty() -> SubwayTextFieldState.Error - response.size == 1 && subway == response.first().name -> SubwayTextFieldState.Success + subway == response.first().name -> SubwayTextFieldState.Success else -> SubwayTextFieldState.Typing } setSubwayStations(response) diff --git a/feature/profile/src/main/java/com/moya/funch/CreateProflieScreen.kt b/feature/profile/src/main/java/com/moya/funch/CreateProflieScreen.kt index 69258d1f..95f51174 100644 --- a/feature/profile/src/main/java/com/moya/funch/CreateProflieScreen.kt +++ b/feature/profile/src/main/java/com/moya/funch/CreateProflieScreen.kt @@ -1,6 +1,7 @@ package com.moya.funch import androidx.compose.foundation.Image +import androidx.compose.foundation.ScrollState import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.detectTapGestures @@ -35,6 +36,7 @@ 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.focus.FocusManager import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.Color @@ -208,7 +210,8 @@ fun CreateProfileScreen( onSubwayStationChange = onSubwayStationChange, isKeyboardVisible = { isKeyboardVisible = it }, textFieldState = profile.subwayTextFieldState, - subwayStations = profile.subwayStations + subwayStations = profile.subwayStations, + scrollState = scrollState ) } Spacer(modifier = Modifier.height(20.dp)) @@ -276,7 +279,11 @@ private fun NicknameRow(nickname: String, onNicknameChange: (String) -> Unit, is @OptIn(ExperimentalLayoutApi::class) @Composable -private fun JobRow(profile: ProfileUiModel, onSelected: (Job) -> Unit) { +private fun JobRow( + profile: ProfileUiModel, + onSelected: (Job) -> Unit, + focusManager: FocusManager = LocalFocusManager.current +) { Row { FunchSmallLabel(text = ProfileLabel.JOB.labelName) FlowRow( @@ -288,7 +295,10 @@ private fun JobRow(profile: ProfileUiModel, onSelected: (Job) -> Unit) { FunchChip( selected = profile.job == job, enabled = true, - onSelected = { onSelected(job) }, + onSelected = { + onSelected(job) + focusManager.clearFocus() + }, label = { Text( text = job.krName, @@ -322,7 +332,7 @@ private fun JobRow(profile: ProfileUiModel, onSelected: (Job) -> Unit) { @OptIn(ExperimentalLayoutApi::class) @Composable -private fun ClubRow(onSelectClub: (Club) -> Unit) { +private fun ClubRow(onSelectClub: (Club) -> Unit, focusManager: FocusManager = LocalFocusManager.current) { Row { FunchSmallLabel(text = ProfileLabel.CLUB.labelName) FlowRow( @@ -336,6 +346,7 @@ private fun ClubRow(onSelectClub: (Club) -> Unit) { selected = isSelected, enabled = true, onSelected = { + focusManager.clearFocus() onSelectClub(club) isSelected = !isSelected }, @@ -371,7 +382,11 @@ private fun ClubRow(onSelectClub: (Club) -> Unit) { } @Composable -private fun MbtiRow(profile: ProfileUiModel, onSelectMbti: (MbtiItem) -> Unit) { +private fun MbtiRow( + profile: ProfileUiModel, + onSelectMbti: (MbtiItem) -> Unit, + focusManager: FocusManager = LocalFocusManager.current +) { val eOrI = profile.eOrI val nOrS = profile.nOrS val tOrF = profile.tOrF @@ -395,6 +410,7 @@ private fun MbtiRow(profile: ProfileUiModel, onSelectMbti: (MbtiItem) -> Unit) { mbtiItem = mbti, isSelected = currentMbti[i] == mbti, onSelected = { + focusManager.clearFocus() onSelectMbti(it) } ) @@ -431,7 +447,7 @@ private fun MbtiButton(mbtiItem: MbtiItem, isSelected: Boolean, onSelected: (Mbt } @Composable -private fun BooldTypeRow(onSelectBloodType: (Blood) -> Unit) { +private fun BooldTypeRow(onSelectBloodType: (Blood) -> Unit, focusManager: FocusManager = LocalFocusManager.current) { val bloodTypes = Blood.entries.filterNot { it == Blood.IDLE }.map { it.type } var placeHolder by remember { mutableStateOf(bloodTypes[0]) } var isDropDownMenuExpanded by remember { mutableStateOf(false) } @@ -442,7 +458,10 @@ private fun BooldTypeRow(onSelectBloodType: (Blood) -> Unit) { Box { FunchDropDownButton( placeHolder = placeHolder, - onClick = { isDropDownMenuExpanded = !isDropDownMenuExpanded }, + onClick = { + focusManager.clearFocus() + isDropDownMenuExpanded = !isDropDownMenuExpanded + }, isDropDownMenuExpanded = isDropDownMenuExpanded, indication = null, modifier = Modifier.onGloballyPositioned { coordinates -> @@ -470,12 +489,19 @@ private fun SubwayRow( onSubwayStationChange: (String) -> Unit, isKeyboardVisible: (Boolean) -> Unit, textFieldState: SubwayTextFieldState, - subwayStations: List + subwayStations: List, + scrollState: ScrollState ) { val interactionSource = remember { MutableInteractionSource() } val isFocused by interactionSource.collectIsFocusedAsState() val focusManager = LocalFocusManager.current + if (isFocused) { + LaunchedEffect(subwayStation) { + scrollState.animateScrollTo(scrollState.maxValue) + } + } + Row { FunchLargeLabel(text = ProfileLabel.SUBWAY.labelName) Column(modifier = Modifier.height(97.dp)) { @@ -484,7 +510,9 @@ private fun SubwayRow( isKeyboardVisible(focusState.isFocused) }, value = subwayStation, - onValueChange = onSubwayStationChange, + onValueChange = { subway -> + onSubwayStationChange(subway) + }, hint = stringResource(id = R.string.subway_textfield_hint), isError = textFieldState == SubwayTextFieldState.Error, iconType = FunchIcon( @@ -505,8 +533,17 @@ private fun SubwayRow( ) when (textFieldState) { - is SubwayTextFieldState.Empty -> { /* @Gun Hyung : 아무것도 표시되지 않음 */ } - is SubwayTextFieldState.Success -> { /* @Gun Hyung : 아무것도 표시되지 않음 */ } + is SubwayTextFieldState.Empty -> { + /* @Gun Hyung : 아무것도 표시되지 않음 */ + } + + is SubwayTextFieldState.Success -> { + HorizontalSubwayStations( + subwayStations = subwayStations, + onSubwayStationChange = onSubwayStationChange + ) + } + is SubwayTextFieldState.Error -> { FunchErrorCaption( modifier = Modifier @@ -517,46 +554,57 @@ private fun SubwayRow( errorText = stringResource(id = R.string.subway_error_caption) ) } + is SubwayTextFieldState.Typing -> { - Spacer(modifier = Modifier.height(4.dp)) - Row( - modifier = Modifier - .fillMaxWidth() - .horizontalScroll(rememberScrollState()), - horizontalArrangement = Arrangement.spacedBy(4.dp) - ) { - subwayStations.forEach { station -> - Box( - modifier = Modifier - .background( - color = Gray800, - shape = FunchTheme.shapes.extraLarge - ) - .clip(FunchTheme.shapes.extraLarge) - .clickable( - onClick = { - onSubwayStationChange(station.name) - focusManager.clearFocus() - }, - interactionSource = remember { MutableInteractionSource() }, - indication = null - ) - .padding(8.dp) - ) { - Text( - text = station.name, - color = White, - style = FunchTheme.typography.b - ) - } - } - } + HorizontalSubwayStations( + subwayStations = subwayStations, + onSubwayStationChange = onSubwayStationChange + ) } } } } } +@Composable +private fun HorizontalSubwayStations(subwayStations: List, onSubwayStationChange: (String) -> Unit) { + val focusManager = LocalFocusManager.current + + Spacer(modifier = Modifier.height(4.dp)) + Row( + modifier = Modifier + .fillMaxWidth() + .horizontalScroll(rememberScrollState()), + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + subwayStations.forEach { station -> + Box( + modifier = Modifier + .background( + color = Gray800, + shape = FunchTheme.shapes.extraLarge + ) + .clip(FunchTheme.shapes.extraLarge) + .clickable( + onClick = { + onSubwayStationChange(station.name) + focusManager.clearFocus() + }, + interactionSource = remember { MutableInteractionSource() }, + indication = null + ) + .padding(8.dp) + ) { + Text( + text = station.name, + color = White, + style = FunchTheme.typography.b + ) + } + } + } +} + @Composable private fun BottomBar(backgroundColor: Color, isCreateProfile: Boolean, onCreateProfile: () -> Unit) { Box( @@ -630,7 +678,8 @@ private fun Preview2() { subwayStations = listOf( SubwayStation("삼성역"), SubwayStation("삼성중앙역") - ) + ), + scrollState = rememberScrollState() ) } } diff --git a/feature/profile/src/main/java/com/moya/funch/navigation/MyProfileNavigatoin.kt b/feature/profile/src/main/java/com/moya/funch/navigation/MyProfileNavigatoin.kt index e64f244f..5671809d 100644 --- a/feature/profile/src/main/java/com/moya/funch/navigation/MyProfileNavigatoin.kt +++ b/feature/profile/src/main/java/com/moya/funch/navigation/MyProfileNavigatoin.kt @@ -13,7 +13,10 @@ const val PROFILE_GRAPH_ROUTE = "profile_graph" fun NavController.navigateToMyProfile(navOptions: NavOptions? = null) = navigate(ProfileScreens.MyProfile.route, navOptions) -fun NavController.closeMyProfile() = popBackStack() +fun NavController.navigateToCreateProfile(navOptions: NavOptions? = null) = + navigate(ProfileScreens.Create.route, navOptions) + +fun NavController.onCloseMyProfile() = popBackStack() fun NavGraphBuilder.profileGraph(onNavigateToHome: () -> Unit, onCloseMyProfile: () -> Unit) { navigation(