diff --git a/build-logic/src/main/java/terning.android.feature.gradle.kts b/build-logic/src/main/java/terning.android.feature.gradle.kts index 1793f9dc8..9575403d0 100644 --- a/build-logic/src/main/java/terning.android.feature.gradle.kts +++ b/build-logic/src/main/java/terning.android.feature.gradle.kts @@ -36,4 +36,5 @@ dependencies { implementation(libs.findLibrary("lottie").get()) implementation(libs.findLibrary("accompanist.systemuicontroller").get()) implementation(libs.findLibrary("androidx.browser").get()) + implementation(libs.findLibrary("kotlin.collections.immutable").get()) } diff --git a/core/designsystem/docs.md b/core/designsystem/docs.md index be01e6847..aff6290bc 100644 --- a/core/designsystem/docs.md +++ b/core/designsystem/docs.md @@ -16,6 +16,9 @@ contentDescription을 사용하지 않는 Image가 담긴 패키지입니다. # Package com.terning.core.designsystem.component.item 여러 화면에서 쓰이는 아이템들이 들어있는 패키지입니다. +# Package com.terning.core.designsystem.component.snackbar +스낵바가 들어있는 패키지입니다. + # Package com.terning.core.designsystem.component.textfield 텍스트 필드가 있는 패키지입니다. @@ -23,7 +26,7 @@ contentDescription을 사용하지 않는 Image가 담긴 패키지입니다. 탑바가 있는 패키지입니다. # Package com.terning.core.designsystem.theme -의 theme가 있는 패키지입니다. +terning의 theme가 있는 패키지입니다. # Package com.terning.core.designsystem.type 여러 가지 타입을 나타내는 패키지입니다. diff --git a/core/designsystem/src/main/java/com/terning/core/designsystem/component/button/RectangleButton.kt b/core/designsystem/src/main/java/com/terning/core/designsystem/component/button/RectangleButton.kt index 32644e98b..d373a28d7 100644 --- a/core/designsystem/src/main/java/com/terning/core/designsystem/component/button/RectangleButton.kt +++ b/core/designsystem/src/main/java/com/terning/core/designsystem/component/button/RectangleButton.kt @@ -44,7 +44,7 @@ fun RectangleButton( @Preview(showBackground = true) @Composable -fun RectangleButtonPreview() { +private fun RectangleButtonPreview() { TerningPointTheme { RectangleButton( style = TerningTheme.typography.button0, diff --git a/core/designsystem/src/main/java/com/terning/core/designsystem/component/button/RoundButton.kt b/core/designsystem/src/main/java/com/terning/core/designsystem/component/button/RoundButton.kt index d826947a4..0b3db5e54 100644 --- a/core/designsystem/src/main/java/com/terning/core/designsystem/component/button/RoundButton.kt +++ b/core/designsystem/src/main/java/com/terning/core/designsystem/component/button/RoundButton.kt @@ -46,7 +46,7 @@ fun RoundButton( @Preview(showBackground = true) @Composable -fun RoundButtonPreview() { +private fun RoundButtonPreview() { TerningPointTheme { RoundButton( style = TerningTheme.typography.button0, diff --git a/core/designsystem/src/main/java/com/terning/core/designsystem/component/button/TerningBasicButton.kt b/core/designsystem/src/main/java/com/terning/core/designsystem/component/button/TerningBasicButton.kt index e450d9132..57d7ba761 100644 --- a/core/designsystem/src/main/java/com/terning/core/designsystem/component/button/TerningBasicButton.kt +++ b/core/designsystem/src/main/java/com/terning/core/designsystem/component/button/TerningBasicButton.kt @@ -57,7 +57,7 @@ fun TerningBasicButton( CompositionLocalProvider(LocalRippleTheme provides NoRippleTheme) { Button( - contentPadding = PaddingValues(paddingVertical), + contentPadding = PaddingValues(vertical = paddingVertical), modifier = modifier.fillMaxWidth(), interactionSource = interactionSource, enabled = isEnabled, @@ -80,7 +80,7 @@ fun TerningBasicButton( @Preview(showBackground = true) @Composable -fun TerningBasicButtonPreview() { +private fun TerningBasicButtonPreview() { TerningPointTheme { TerningBasicButton( text = R.string.button_preview, diff --git a/feature/filtering/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringRoute.kt b/feature/filtering/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringRoute.kt index 0b68f45a8..b3b68134c 100644 --- a/feature/filtering/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringRoute.kt +++ b/feature/filtering/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringRoute.kt @@ -3,9 +3,9 @@ package com.terning.feature.filtering.startfiltering import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -21,17 +21,18 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.airbnb.lottie.compose.LottieCompositionSpec import com.airbnb.lottie.compose.LottieConstants -import com.airbnb.lottie.compose.rememberLottieComposition import com.terning.core.analytics.EventType import com.terning.core.analytics.LocalTracker -import com.terning.core.designsystem.component.button.RectangleButton +import com.terning.core.designsystem.component.button.RoundButton import com.terning.core.designsystem.component.item.TerningLottieAnimation +import com.terning.core.designsystem.extension.noRippleClickable +import com.terning.core.designsystem.theme.Grey500 import com.terning.core.designsystem.theme.TerningPointTheme import com.terning.core.designsystem.theme.TerningTheme import com.terning.core.designsystem.theme.White @@ -40,7 +41,8 @@ import kotlinx.coroutines.delay @Composable fun StartFilteringRoute( - onNextClick: () -> Unit, + onStartClick: () -> Unit, + onLaterClick: () -> Unit, viewModel: StartFilteringViewModel = hiltViewModel() ) { val state by viewModel.state.collectAsStateWithLifecycle() @@ -56,69 +58,90 @@ fun StartFilteringRoute( } StartFilteringScreen( - onNextClick = { - onNextClick() + onStartClick = { + onStartClick() amplitudeTracker.track( type = EventType.CLICK, name = "start_service" ) }, + onLaterClick = onLaterClick, buttonState = state.isButtonVisible, screenHeight = screenHeight, ) - } @Composable fun StartFilteringScreen( - onNextClick: () -> Unit, + onStartClick: () -> Unit, + onLaterClick: () -> Unit, buttonState: Boolean, screenHeight: Float, ) { - Box( + Column( modifier = Modifier .fillMaxSize() .statusBarsPadding() .navigationBarsPadding() - .background(White) + .background(White), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height((138 * screenHeight).dp)) + Text( + text = stringResource(id = R.string.start_filtering_title), + style = TerningTheme.typography.title1, + textAlign = TextAlign.Center + ) + TerningLottieAnimation( + jsonFile = R.raw.terning_onboarding_start, + iterations = LottieConstants.IterateForever, + modifier = Modifier + .fillMaxWidth() + .aspectRatio(1f) + .padding(horizontal = 24.dp) + ) + Spacer(modifier = Modifier.height((79 * screenHeight).dp)) + ButtonAnimation( + buttonState = buttonState, + onStartClick = onStartClick, + onLaterClick = onLaterClick + ) + Spacer(modifier = Modifier.height((24 * screenHeight).dp)) + } +} + +@Composable +private fun ButtonAnimation( + buttonState: Boolean, + onStartClick: () -> Unit, + onLaterClick: () -> Unit, + modifier: Modifier = Modifier, +) { + AnimatedVisibility( + visible = buttonState, + enter = fadeIn(initialAlpha = 0.3f), ) { Column( - modifier = Modifier.fillMaxSize(), + modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally ) { - Spacer(modifier = Modifier.height((128 * screenHeight).dp)) - Text( - text = stringResource(id = R.string.start_filtering_title), - style = TerningTheme.typography.title1, - modifier = Modifier.padding(bottom = 36.dp), - textAlign = TextAlign.Center + RoundButton( + style = TerningTheme.typography.button0, + paddingVertical = 17.dp, + text = R.string.start_filtering_button, + onButtonClick = onStartClick, + cornerRadius = 10.dp, + modifier = Modifier.padding(horizontal = 24.dp) ) - TerningLottieAnimation( - jsonFile = R.raw.terning_onboarding_start, - iterations = LottieConstants.IterateForever, - modifier = Modifier - .fillMaxWidth() - .height(372.dp) - .padding(horizontal = 24.dp) + Spacer(modifier = Modifier.height(12.dp)) + Text( + text = stringResource(R.string.start_filtering_later), + style = TerningTheme.typography.detail3.copy( + textDecoration = TextDecoration.Underline + ), + color = Grey500, + modifier = Modifier.noRippleClickable { onLaterClick() } ) - Spacer(modifier = Modifier.weight(2f)) - } - Box( - modifier = Modifier - .align(Alignment.BottomCenter) - .padding(bottom = 12.dp) - ) { - AnimatedVisibility( - visible = buttonState, - enter = fadeIn(initialAlpha = 0.3f), - ) { - RectangleButton( - style = TerningTheme.typography.button0, - paddingVertical = 20.dp, - text = R.string.start_filtering_button, - onButtonClick = onNextClick, - ) - } } } } @@ -127,10 +150,11 @@ private const val DELAY: Long = 1000 @Preview(showBackground = true) @Composable -fun StartFilteringScreenPreview() { +private fun StartFilteringScreenPreview() { TerningPointTheme { StartFilteringScreen( - onNextClick = {}, + onStartClick = {}, + onLaterClick = {}, buttonState = true, screenHeight = 1f ) diff --git a/feature/filtering/src/main/java/com/terning/feature/filtering/startfiltering/navigation/StartFilteringNavigation.kt b/feature/filtering/src/main/java/com/terning/feature/filtering/startfiltering/navigation/StartFilteringNavigation.kt index 12b8a4cdc..f6e79b5d7 100644 --- a/feature/filtering/src/main/java/com/terning/feature/filtering/startfiltering/navigation/StartFilteringNavigation.kt +++ b/feature/filtering/src/main/java/com/terning/feature/filtering/startfiltering/navigation/StartFilteringNavigation.kt @@ -2,12 +2,10 @@ package com.terning.feature.filtering.startfiltering.navigation import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.NavOptions import androidx.navigation.compose.composable import androidx.navigation.toRoute import com.terning.core.navigation.Route -import com.terning.feature.filtering.filteringone.navigation.navigateFilteringOne import com.terning.feature.filtering.startfiltering.StartFilteringRoute import kotlinx.serialization.Serializable @@ -22,14 +20,14 @@ fun NavController.navigateStartFiltering( } fun NavGraphBuilder.startFilteringNavGraph( - navHostController: NavHostController + onStartClick: (String) -> Unit, + onLaterClick: () -> Unit ) { composable { val args = it.toRoute() StartFilteringRoute( - onNextClick = { - navHostController.navigateFilteringOne(args.name) - } + onStartClick = { onStartClick(args.name) }, + onLaterClick = onLaterClick ) } } diff --git a/feature/filtering/src/main/java/com/terning/feature/filtering/starthome/StartHomeRoute.kt b/feature/filtering/src/main/java/com/terning/feature/filtering/starthome/StartHomeRoute.kt index ba3765118..02113fb92 100644 --- a/feature/filtering/src/main/java/com/terning/feature/filtering/starthome/StartHomeRoute.kt +++ b/feature/filtering/src/main/java/com/terning/feature/filtering/starthome/StartHomeRoute.kt @@ -3,7 +3,6 @@ package com.terning.feature.filtering.starthome import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio @@ -26,11 +25,9 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.airbnb.lottie.compose.LottieAnimation -import com.airbnb.lottie.compose.LottieCompositionSpec import com.airbnb.lottie.compose.LottieConstants -import com.airbnb.lottie.compose.rememberLottieComposition -import com.terning.core.designsystem.component.button.RectangleButton +import com.terning.core.designsystem.component.button.RoundButton +import com.terning.core.designsystem.component.item.TerningLottieAnimation import com.terning.core.designsystem.theme.TerningPointTheme import com.terning.core.designsystem.theme.TerningTheme import com.terning.core.designsystem.theme.White @@ -53,7 +50,7 @@ fun StartHomeRoute( } StartHomeScreen( - navigateToHome = navigateToHome, + onClick = navigateToHome, buttonState = state.isButtonVisible, screenHeight = screenHeight, ) @@ -61,81 +58,59 @@ fun StartHomeRoute( @Composable fun StartHomeScreen( - navigateToHome: () -> Unit, + onClick: () -> Unit, buttonState: Boolean, screenHeight: Float, ) { - Box( + Column( modifier = Modifier .fillMaxSize() - .background(White) .statusBarsPadding() .navigationBarsPadding() + .background(White), + horizontalAlignment = Alignment.CenterHorizontally ) { - Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Spacer(modifier = Modifier.height((128 * screenHeight).dp)) - Text( - text = stringResource(id = R.string.start_home_title), - style = TerningTheme.typography.title1, - textAlign = TextAlign.Center, - modifier = Modifier.padding(bottom = 30.dp) - ) - StartHomeLottieAnimation() - } - Box( + Spacer(modifier = Modifier.height((128 * screenHeight).dp)) + Text( + text = stringResource(id = R.string.start_home_title), + style = TerningTheme.typography.title1, + textAlign = TextAlign.Center, + modifier = Modifier.padding(bottom = 52.dp) + ) + TerningLottieAnimation( + jsonFile = R.raw.terning_start_home, modifier = Modifier - .fillMaxSize() - .padding(bottom = 12.dp), - contentAlignment = Alignment.BottomCenter + .fillMaxWidth() + .padding(horizontal = 24.dp) + .aspectRatio(1f), + iterations = LottieConstants.IterateForever + ) + Spacer(modifier = Modifier.height((88 * screenHeight).dp)) + AnimatedVisibility( + visible = buttonState, + enter = fadeIn(initialAlpha = 0.3f), ) { - AnimatedVisibility( - visible = buttonState, - enter = fadeIn(initialAlpha = 0.3f), - ) { - RectangleButton( - style = TerningTheme.typography.button0, - paddingVertical = 20.dp, - text = R.string.start_home_next_button, - onButtonClick = navigateToHome, - ) - } + RoundButton( + style = TerningTheme.typography.button0, + paddingVertical = 17.dp, + text = R.string.start_home_next_button, + onButtonClick = onClick, + cornerRadius = 10.dp, + modifier = Modifier.padding(horizontal = 24.dp) + ) } + Spacer(modifier = Modifier.height((49 * screenHeight).dp)) } } private const val DELAY: Long = 1000 -@Composable -fun StartHomeLottieAnimation( - modifier: Modifier = Modifier -) { - val lottieComposition by rememberLottieComposition( - LottieCompositionSpec.RawRes(R.raw.terning_start_home) - ) - LottieAnimation( - modifier = modifier - .fillMaxWidth() - .aspectRatio( - (lottieComposition?.bounds - ?.width() - ?.toFloat() - ?: 1f) / (lottieComposition?.bounds?.height() ?: 1) - ) - .padding(horizontal = 24.dp), - composition = lottieComposition, - iterations = LottieConstants.IterateForever - ) -} - @Preview(showBackground = true) @Composable -fun StartHomeScreenPreview() { +private fun StartHomeScreenPreview() { TerningPointTheme { StartHomeScreen( - navigateToHome = {}, + onClick = {}, buttonState = true, screenHeight = 1f ) diff --git a/feature/filtering/src/main/res/values/strings.xml b/feature/filtering/src/main/res/values/strings.xml index a32446029..858e1d62c 100644 --- a/feature/filtering/src/main/res/values/strings.xml +++ b/feature/filtering/src/main/res/values/strings.xml @@ -7,6 +7,7 @@ 터닝에서 내 계획에 딱 맞는\n대학생 인턴 찾기를 도와드릴게요 시작화면 이미지 시작하기 + 계획 나중에 입력하기 나에게 딱 맞는 공고가 준비됐어요!\n터닝을 시작해 볼까요? diff --git a/feature/main/src/main/java/com/terning/feature/main/MainScreen.kt b/feature/main/src/main/java/com/terning/feature/main/MainScreen.kt index 2f28f4a4f..e51797e72 100644 --- a/feature/main/src/main/java/com/terning/feature/main/MainScreen.kt +++ b/feature/main/src/main/java/com/terning/feature/main/MainScreen.kt @@ -45,6 +45,7 @@ import com.terning.core.designsystem.theme.White import com.terning.core.designsystem.util.NoRippleInteractionSource import com.terning.feature.calendar.calendar.navigation.calendarNavGraph import com.terning.feature.filtering.filteringone.navigation.filteringOneNavGraph +import com.terning.feature.filtering.filteringone.navigation.navigateFilteringOne import com.terning.feature.filtering.filteringthree.navigation.filteringThreeNavGraph import com.terning.feature.filtering.filteringtwo.navigation.filteringTwoNavGraph import com.terning.feature.filtering.startfiltering.navigation.navigateStartFiltering @@ -65,6 +66,8 @@ import com.terning.feature.onboarding.splash.navigation.splashNavGraph import com.terning.feature.search.search.navigation.searchNavGraph import com.terning.feature.search.searchprocess.navigation.navigateSearchProcess import com.terning.feature.search.searchprocess.navigation.searchProcessNavGraph +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.launch @Composable @@ -112,7 +115,7 @@ fun MainScreen( bottomBar = { MainBottomBar( isVisible = navigator.showBottomBar(), - tabs = MainTab.entries.toList(), + tabs = MainTab.entries.toImmutableList(), currentTab = navigator.currentTab, onTabSelected = { navigation -> amplitudeTracker.track( @@ -221,7 +224,19 @@ fun MainScreen( ) } ) - startFilteringNavGraph(navHostController = navigator.navController) + startFilteringNavGraph( + onStartClick = { name -> + navigator.navController.navigateFilteringOne(name) + }, + onLaterClick = { + val navOptions = navOptions { + popUpTo(id = navigator.navController.graph.id) { + inclusive = true + } + } + navigator.navController.navigateHome(navOptions) + } + ) startHomeNavGraph( navigateHome = { val navOptions = navOptions { @@ -265,7 +280,7 @@ fun MainScreen( @Composable private fun MainBottomBar( isVisible: Boolean, - tabs: List, + tabs: ImmutableList, currentTab: MainTab?, onTabSelected: (MainTab) -> Unit, ) { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ba743bca2..78642c01a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -46,6 +46,7 @@ kotlinxImmutable = "0.3.7" kotlinxSerializationJsonVersion = "1.5.1" serialization = "1.6.3" kotlinParcelize = "1.8.20" +kotlinx-collections-immutable = "0.3.8" ## Coroutine coroutine = "1.8.1" @@ -158,6 +159,7 @@ coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutin coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutine" } kotlin = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" } kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +kotlin-collections-immutable = { group = "org.jetbrains.kotlinx", name = "kotlinx-collections-immutable", version.ref = "kotlinx-collections-immutable" } coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coilCompose" } coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coilCompose" } @@ -167,7 +169,6 @@ androidx-test-espresso-core = { group = "androidx.test.espresso", name = "espres material = { group = "com.google.android.material", name = "material", version.ref = "material" } timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } -ossLicense = { group = "com.google.android.gms", name = "play-services-oss-licenses", version.ref = "ossVersion" } lottie = { group = "com.airbnb.android", name = "lottie-compose", version.ref = "lottieVersion" } kakao-user = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakaoVersion" }