From bea52080e3bd0d994f71783558e7fa2565983111 Mon Sep 17 00:00:00 2001 From: tgyuuAn Date: Sat, 22 Nov 2025 17:34:23 +0900 Subject: [PATCH 1/2] temp --- .../puzzle/designsystem/component/TopBar.kt | 87 +++---- .../main/java/com/puzzle/navigation/Route.kt | 13 +- .../com/puzzle/navigation/TopBarConfig.kt | 18 ++ .../matching/graph/block/BlockScreen.kt | 2 +- .../matching/graph/contact/ContactScreen.kt | 4 +- .../graph/detail/MatchingDetailScreen.kt | 2 +- .../matching/graph/main/MatchingScreen.kt | 92 ++++--- .../matching/graph/main/MatchingViewModel.kt | 3 +- .../graph/preview/ProfilePreviewScreen.kt | 2 +- .../matching/graph/report/ReportScreen.kt | 2 +- .../matching/navigation/MatchingNavigation.kt | 4 +- .../notification/NotificationViewModel.kt | 4 +- .../register/RegisterProfileViewModel.kt | 3 +- .../main/java/com/puzzle/store/StoreScreen.kt | 45 +++- .../java/com/puzzle/store/StoreViewModel.kt | 1 - .../com/puzzle/store/contract/StoreIntent.kt | 1 - .../store/navigation/StoreNavigation.kt | 4 +- .../puzzle/store/ui/components/StoreTopBar.kt | 89 ------- .../ui/page/CompactStoreLoadingScreen.kt | 13 +- .../store/ui/page/CompactStoreScreen.kt | 11 +- .../ui/page/ExpandedStoreLoadingScreen.kt | 9 +- .../store/ui/page/ExpandedStoreScreen.kt | 10 +- gradle.properties | 2 +- .../com/puzzle/presentation/MainActivity.kt | 6 +- .../com/puzzle/presentation/MainViewModel.kt | 4 +- .../presentation/navigation/AppBottomBar.kt | 4 +- .../presentation/navigation/AppNavHost.kt | 5 +- .../presentation/navigation/MainNavGraph.kt | 225 ++++++++++++++++++ .../navigation/TopLevelDestinvation.kt | 4 +- .../com/puzzle/presentation/ui/AppState.kt | 2 +- .../com/puzzle/presentation/ui/AppUiPolicy.kt | 11 +- .../com/puzzle/presentation/ui/PieceApp.kt | 4 +- 32 files changed, 418 insertions(+), 268 deletions(-) create mode 100644 core/navigation/src/main/java/com/puzzle/navigation/TopBarConfig.kt delete mode 100644 feature/store/src/main/java/com/puzzle/store/ui/components/StoreTopBar.kt create mode 100644 presentation/src/main/java/com/puzzle/presentation/navigation/MainNavGraph.kt diff --git a/core/designsystem/src/main/java/com/puzzle/designsystem/component/TopBar.kt b/core/designsystem/src/main/java/com/puzzle/designsystem/component/TopBar.kt index f829e47d2..87e6447cc 100644 --- a/core/designsystem/src/main/java/com/puzzle/designsystem/component/TopBar.kt +++ b/core/designsystem/src/main/java/com/puzzle/designsystem/component/TopBar.kt @@ -1,5 +1,12 @@ package com.puzzle.designsystem.component +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.tween +import androidx.compose.animation.expandHorizontally +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.shrinkHorizontally import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -10,9 +17,11 @@ 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.wrapContentHeight 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 @@ -125,63 +134,57 @@ fun PieceSubCloseTopBar( } @Composable -fun PiecePuzzleTopBar( +fun PiecePuzzleChip( count: Int, chipColor: Color, contentColor: Color, modifier: Modifier = Modifier, isShowPlus: Boolean = true, - leftComponent: @Composable () -> Unit = {}, - rightComponent: @Composable () -> Unit = {}, - onStoreClick: () -> Unit = {}, + onChipClick: () -> Unit = {}, ) { + val endPadding by animateDpAsState( + targetValue = if (isShowPlus) 8.dp else 16.dp, + animationSpec = tween(700), + label = "endPadding" + ) + Row( + horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, modifier = modifier - .fillMaxWidth() - .height(60.dp) - .clickable(isShowPlus) { onStoreClick() }, + .wrapContentHeight() + .clip(RoundedCornerShape(24.dp)) + .background(chipColor) + .clickable(isShowPlus) { onChipClick() } + .padding(start = 12.dp, end = endPadding, top = 8.dp, bottom = 8.dp), ) { - leftComponent() - - val endPadding = if (isShowPlus) 8.dp else 16.dp - Row( - horizontalArrangement = Arrangement.spacedBy( - space = 4.dp, - alignment = Alignment.CenterHorizontally - ), - verticalAlignment = Alignment.CenterVertically, + Image( + painter = painterResource(R.drawable.ic_puzzle), + contentDescription = null, + colorFilter = ColorFilter.tint(contentColor), modifier = Modifier - .clip(RoundedCornerShape(24.dp)) - .background(chipColor) - .padding(start = 12.dp, end = endPadding, top = 8.dp, bottom = 8.dp), + .size(24.dp) + .padding(end = 4.dp), + ) + + Text( + text = count.toString(), + style = PieceTheme.typography.headingSSB, + color = contentColor, + modifier = Modifier.padding(end = 4.dp), + ) + + AnimatedVisibility( + visible = isShowPlus, + enter = fadeIn(animationSpec = tween(700)) + expandHorizontally(tween(700)), + exit = fadeOut(animationSpec = tween(700)) + shrinkHorizontally(tween(700)) ) { Image( - painter = painterResource(R.drawable.ic_puzzle), + painter = painterResource(R.drawable.ic_plus), contentDescription = null, - colorFilter = ColorFilter.tint(contentColor), - modifier = Modifier.size(24.dp), - ) - - Text( - text = count.toString(), - style = PieceTheme.typography.headingSSB, - color = contentColor, + colorFilter = ColorFilter.tint(PieceTheme.colors.dark3), ) - - if (isShowPlus) { - Image( - painter = painterResource(R.drawable.ic_plus), - contentDescription = null, - colorFilter = ColorFilter.tint(PieceTheme.colors.dark3), - modifier = Modifier.padding(start = 4.dp), - ) - } } - - Spacer(modifier = Modifier.weight(1f)) - - rightComponent() } } @@ -261,7 +264,7 @@ fun PreviewPieceSubCloseTopBar() { @Composable fun PreviewPiecePuzzleTopBar() { PieceTheme { - PiecePuzzleTopBar( + PiecePuzzleChip( count = 22, chipColor = PieceTheme.colors.white.copy(alpha = 0.1f), contentColor = PieceTheme.colors.black, diff --git a/core/navigation/src/main/java/com/puzzle/navigation/Route.kt b/core/navigation/src/main/java/com/puzzle/navigation/Route.kt index 431f15318..c48081e49 100644 --- a/core/navigation/src/main/java/com/puzzle/navigation/Route.kt +++ b/core/navigation/src/main/java/com/puzzle/navigation/Route.kt @@ -33,7 +33,15 @@ sealed interface SettingGraph : Route { } @Serializable -data object MatchingGraphBaseRoute : Route +data object MainNavGraphBaseRoute : Route + +sealed interface MainGraph : Route { + @Serializable + data object MatchingGraphBaseRoute : MainGraph + + @Serializable + data object StoreRoute : MainGraph +} sealed interface MatchingGraph : Route { @Serializable @@ -84,8 +92,5 @@ data object PauseRoute : Route @Serializable data object NotificationRoute : Route -@Serializable -data object StoreRoute : Route - @Serializable data object AnalyticsRoute : Route diff --git a/core/navigation/src/main/java/com/puzzle/navigation/TopBarConfig.kt b/core/navigation/src/main/java/com/puzzle/navigation/TopBarConfig.kt new file mode 100644 index 000000000..9b901cae3 --- /dev/null +++ b/core/navigation/src/main/java/com/puzzle/navigation/TopBarConfig.kt @@ -0,0 +1,18 @@ +package com.puzzle.navigation + +import androidx.compose.runtime.Immutable + +@Immutable +sealed interface TopBarType { + data object None : TopBarType + data class Main( + val onNotificationClick: () -> Unit + ) : TopBarType + data class Puzzle( + val puzzleCount: Int, + val showBackButton: Boolean, + val onBackClick: () -> Unit = {}, + val onPuzzleClick: () -> Unit, + val onNotificationClick: () -> Unit + ) : TopBarType +} diff --git a/feature/matching/src/main/java/com/puzzle/matching/graph/block/BlockScreen.kt b/feature/matching/src/main/java/com/puzzle/matching/graph/block/BlockScreen.kt index 5fab611c3..fa537f5bd 100644 --- a/feature/matching/src/main/java/com/puzzle/matching/graph/block/BlockScreen.kt +++ b/feature/matching/src/main/java/com/puzzle/matching/graph/block/BlockScreen.kt @@ -31,7 +31,7 @@ import com.puzzle.matching.graph.block.dialog.BlockDialog import com.puzzle.matching.graph.block.dialog.BlockDoneDialog @Composable -internal fun BlockRoute(viewModel: BlockViewModel = hiltViewModel()) { +fun BlockRoute(viewModel: BlockViewModel = hiltViewModel()) { val state by viewModel.state.collectAsStateWithLifecycle() BlockScreen( diff --git a/feature/matching/src/main/java/com/puzzle/matching/graph/contact/ContactScreen.kt b/feature/matching/src/main/java/com/puzzle/matching/graph/contact/ContactScreen.kt index 3c265568b..4b518b5c5 100644 --- a/feature/matching/src/main/java/com/puzzle/matching/graph/contact/ContactScreen.kt +++ b/feature/matching/src/main/java/com/puzzle/matching/graph/contact/ContactScreen.kt @@ -62,7 +62,7 @@ import com.puzzle.matching.graph.contact.contract.getUnSelectedContactIconId import kotlinx.coroutines.delay @Composable -internal fun ContactRoute( +fun ContactRoute( viewModel: ContactViewModel = hiltViewModel(), ) { val state by viewModel.state.collectAsStateWithLifecycle() @@ -131,7 +131,7 @@ private fun ContactScreen( modifier = Modifier .clip(RoundedCornerShape(8.dp)) .size(220.dp) - .then ( + .then( if (state.imageUrl.isNotEmpty()) Modifier.background(PieceTheme.colors.black) else Modifier ) diff --git a/feature/matching/src/main/java/com/puzzle/matching/graph/detail/MatchingDetailScreen.kt b/feature/matching/src/main/java/com/puzzle/matching/graph/detail/MatchingDetailScreen.kt index 2e566a69b..e53927d2a 100644 --- a/feature/matching/src/main/java/com/puzzle/matching/graph/detail/MatchingDetailScreen.kt +++ b/feature/matching/src/main/java/com/puzzle/matching/graph/detail/MatchingDetailScreen.kt @@ -56,7 +56,7 @@ import com.puzzle.matching.graph.detail.page.ValuePickPage import com.puzzle.matching.graph.detail.page.ValueTalkPage @Composable -internal fun MatchingDetailRoute(viewModel: MatchingDetailViewModel = hiltViewModel()) { +fun MatchingDetailRoute(viewModel: MatchingDetailViewModel = hiltViewModel()) { val state by viewModel.state.collectAsStateWithLifecycle() MatchingDetailScreen( diff --git a/feature/matching/src/main/java/com/puzzle/matching/graph/main/MatchingScreen.kt b/feature/matching/src/main/java/com/puzzle/matching/graph/main/MatchingScreen.kt index b099acad6..cc3708fb0 100644 --- a/feature/matching/src/main/java/com/puzzle/matching/graph/main/MatchingScreen.kt +++ b/feature/matching/src/main/java/com/puzzle/matching/graph/main/MatchingScreen.kt @@ -2,23 +2,21 @@ package com.puzzle.matching.graph.main import android.app.Activity import androidx.activity.compose.BackHandler -import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column +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.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel @@ -26,12 +24,10 @@ import androidx.lifecycle.compose.LifecycleStartEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.puzzle.analytics.TrackScreenViewEvent import com.puzzle.common.event.PieceEvent +import com.puzzle.common.ui.LocalSystemBarState import com.puzzle.common.ui.SnackBarState import com.puzzle.common.ui.blur -import com.puzzle.common.ui.clickable import com.puzzle.designsystem.R -import com.puzzle.designsystem.component.PieceMainTopBar -import com.puzzle.designsystem.component.PiecePuzzleTopBar import com.puzzle.designsystem.foundation.PieceTheme import com.puzzle.domain.model.match.MatchInfo import com.puzzle.domain.model.match.MatchStatus.WAITING @@ -45,12 +41,45 @@ import com.puzzle.matching.graph.main.page.MatchingUserScreen import com.puzzle.matching.graph.main.page.MatchingWaitingScreen @Composable -internal fun MatchingRoute( +fun MatchingRoute( viewModel: MatchingViewModel = hiltViewModel(), + onTopBarConfigChange: (com.puzzle.navigation.TopBarType) -> Unit = {}, + onNavigateToStore: () -> Unit = {}, ) { val state by viewModel.state.collectAsStateWithLifecycle() var backPressedTime by remember { mutableLongStateOf(0L) } val context = LocalContext.current + val systemBarState = LocalSystemBarState.current + val blackColor = PieceTheme.colors.black + + // Set StatusBar color to black for Matching screens + LaunchedEffect(Unit) { + systemBarState.setStatusBarColor(blackColor) + } + + // Update TopBar config based on matching status + LaunchedEffect(state.matchingStatus) { + when (state.matchingStatus) { + MatchingState.MatchingStatus.LOADING, MatchingState.MatchingStatus.PENDING -> { + onTopBarConfigChange( + com.puzzle.navigation.TopBarType.Main( + onNotificationClick = { viewModel.onIntent(MatchingIntent.OnNotificationClick) } + ) + ) + } + + MatchingState.MatchingStatus.USER, MatchingState.MatchingStatus.WAITING -> { + onTopBarConfigChange( + com.puzzle.navigation.TopBarType.Puzzle( + puzzleCount = 22, // TODO: Get actual puzzle count + showBackButton = false, + onPuzzleClick = onNavigateToStore, + onNotificationClick = { viewModel.onIntent(MatchingIntent.OnNotificationClick) } + ) + ) + } + } + } LifecycleStartEffect(viewModel) { viewModel.initMatchInfo() @@ -74,7 +103,7 @@ internal fun MatchingRoute( MatchingScreen( state = state, onButtonClick = { viewModel.onIntent(MatchingIntent.OnButtonClick) }, - onStoreClick = { viewModel.onIntent(MatchingIntent.OnStoreClick) }, + onStoreClick = onNavigateToStore, onMatchingDetailClick = { viewModel.onIntent(MatchingIntent.OnMatchingDetailClick) }, onCheckMyProfileClick = { viewModel.onIntent(MatchingIntent.OnCheckMyProfileClick) }, onEditProfileClick = { viewModel.onIntent(MatchingIntent.OnEditProfileClick) }, @@ -112,49 +141,10 @@ internal fun MatchingScreen( .blur(isBlur = isShowDialog) .padding(horizontal = 20.dp) ) { - // TopBar - when (state.matchingStatus) { - MatchingStatus.LOADING, MatchingStatus.PENDING -> { - PieceMainTopBar( - title = stringResource(R.string.matching_title), - textStyle = PieceTheme.typography.branding, - titleColor = PieceTheme.colors.white, - rightComponent = { - Image( - painter = painterResource(R.drawable.ic_alarm_black), - contentDescription = "알람", - colorFilter = ColorFilter.tint(PieceTheme.colors.white), - modifier = Modifier - .size(32.dp) - .clickable { onNotificationClick() }, - ) - }, - modifier = Modifier.padding(bottom = 16.dp), - ) - } - - MatchingStatus.USER, MatchingStatus.WAITING -> { - PiecePuzzleTopBar( - count = 22, - chipColor = PieceTheme.colors.white.copy(alpha = 0.1f), - contentColor = PieceTheme.colors.white, - rightComponent = { - Image( - painter = painterResource(R.drawable.ic_alarm_black), - contentDescription = "알람", - colorFilter = ColorFilter.tint(PieceTheme.colors.white), - modifier = Modifier - .size(32.dp) - .clickable { onNotificationClick() }, - ) - }, - onStoreClick = { onStoreClick() }, - modifier = Modifier.padding(bottom = 16.dp), - ) - } - } + // TopBar is now managed by MainNavGraph // Body + Spacer(modifier = Modifier.height(16.dp)) when (state.matchingStatus) { MatchingStatus.LOADING -> MatchingLoadingScreen() MatchingStatus.PENDING -> MatchingPendingScreen( diff --git a/feature/matching/src/main/java/com/puzzle/matching/graph/main/MatchingViewModel.kt b/feature/matching/src/main/java/com/puzzle/matching/graph/main/MatchingViewModel.kt index 5ee5f2a1a..15ec9af09 100644 --- a/feature/matching/src/main/java/com/puzzle/matching/graph/main/MatchingViewModel.kt +++ b/feature/matching/src/main/java/com/puzzle/matching/graph/main/MatchingViewModel.kt @@ -27,7 +27,6 @@ import com.puzzle.navigation.NavigationHelper import com.puzzle.navigation.NotificationRoute import com.puzzle.navigation.ProfileGraph import com.puzzle.navigation.SettingGraph -import com.puzzle.navigation.StoreRoute import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Job @@ -59,7 +58,7 @@ class MatchingViewModel @Inject constructor( ) MatchingIntent.OnNotificationClick -> navigationHelper.navigate(To(NotificationRoute)) - MatchingIntent.OnStoreClick -> navigationHelper.navigate(To(StoreRoute)) + MatchingIntent.OnStoreClick -> Unit // Navigation handled by MainNavGraph } } diff --git a/feature/matching/src/main/java/com/puzzle/matching/graph/preview/ProfilePreviewScreen.kt b/feature/matching/src/main/java/com/puzzle/matching/graph/preview/ProfilePreviewScreen.kt index 92c2869c1..57b65acbd 100644 --- a/feature/matching/src/main/java/com/puzzle/matching/graph/preview/ProfilePreviewScreen.kt +++ b/feature/matching/src/main/java/com/puzzle/matching/graph/preview/ProfilePreviewScreen.kt @@ -48,7 +48,7 @@ import com.puzzle.matching.graph.preview.page.ValuePickPage import com.puzzle.matching.graph.preview.page.ValueTalkPage @Composable -internal fun ProfilePreviewRoute( +fun ProfilePreviewRoute( viewModel: ProfilePreviewViewModel = hiltViewModel(), ) { val state by viewModel.state.collectAsStateWithLifecycle() diff --git a/feature/matching/src/main/java/com/puzzle/matching/graph/report/ReportScreen.kt b/feature/matching/src/main/java/com/puzzle/matching/graph/report/ReportScreen.kt index 888c90b51..9de77ed17 100644 --- a/feature/matching/src/main/java/com/puzzle/matching/graph/report/ReportScreen.kt +++ b/feature/matching/src/main/java/com/puzzle/matching/graph/report/ReportScreen.kt @@ -40,7 +40,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch @Composable -internal fun ReportRoute(viewModel: ReportViewModel = hiltViewModel()) { +fun ReportRoute(viewModel: ReportViewModel = hiltViewModel()) { val state by viewModel.state.collectAsStateWithLifecycle() ReportScreen( diff --git a/feature/matching/src/main/java/com/puzzle/matching/navigation/MatchingNavigation.kt b/feature/matching/src/main/java/com/puzzle/matching/navigation/MatchingNavigation.kt index bc227d15b..e375d7bb7 100644 --- a/feature/matching/src/main/java/com/puzzle/matching/navigation/MatchingNavigation.kt +++ b/feature/matching/src/main/java/com/puzzle/matching/navigation/MatchingNavigation.kt @@ -9,11 +9,11 @@ import com.puzzle.matching.graph.detail.MatchingDetailRoute import com.puzzle.matching.graph.main.MatchingRoute import com.puzzle.matching.graph.preview.ProfilePreviewRoute import com.puzzle.matching.graph.report.ReportRoute +import com.puzzle.navigation.MainGraph import com.puzzle.navigation.MatchingGraph -import com.puzzle.navigation.MatchingGraphBaseRoute fun NavGraphBuilder.matchingNavGraph() { - navigation( + navigation( startDestination = MatchingGraph.MatchingRoute, ) { composable { diff --git a/feature/notification/src/main/java/com/puzzle/notification/NotificationViewModel.kt b/feature/notification/src/main/java/com/puzzle/notification/NotificationViewModel.kt index 10685941c..bbfaec2dc 100644 --- a/feature/notification/src/main/java/com/puzzle/notification/NotificationViewModel.kt +++ b/feature/notification/src/main/java/com/puzzle/notification/NotificationViewModel.kt @@ -6,7 +6,7 @@ import com.puzzle.common.suspendRunCatching import com.puzzle.domain.model.error.ErrorHelper import com.puzzle.domain.model.notification.Notification import com.puzzle.domain.repository.NotificationRepository -import com.puzzle.navigation.MatchingGraph +import com.puzzle.navigation.MainNavGraphBaseRoute import com.puzzle.navigation.NavigationEvent import com.puzzle.navigation.NavigationEvent.To import com.puzzle.navigation.NavigationHelper @@ -87,7 +87,7 @@ class NotificationViewModel @Inject constructor( ) } - navigationHelper.navigate(To(MatchingGraph.MatchingRoute)) + navigationHelper.navigate(To(MainNavGraphBaseRoute)) }.onFailure { errorHelper.sendError(it) } } } diff --git a/feature/profile/src/main/java/com/puzzle/profile/graph/register/RegisterProfileViewModel.kt b/feature/profile/src/main/java/com/puzzle/profile/graph/register/RegisterProfileViewModel.kt index 87406f050..355b7d7a7 100644 --- a/feature/profile/src/main/java/com/puzzle/profile/graph/register/RegisterProfileViewModel.kt +++ b/feature/profile/src/main/java/com/puzzle/profile/graph/register/RegisterProfileViewModel.kt @@ -20,6 +20,7 @@ import com.puzzle.domain.model.profile.ValueTalkAnswer import com.puzzle.domain.model.user.UserRole import com.puzzle.domain.repository.ProfileRepository import com.puzzle.domain.repository.UserRepository +import com.puzzle.navigation.MainNavGraphBaseRoute import com.puzzle.navigation.MatchingGraph import com.puzzle.navigation.NavigationEvent import com.puzzle.navigation.NavigationHelper @@ -600,7 +601,7 @@ class RegisterProfileViewModel @Inject constructor( } private fun navigateToHome() { - navigationHelper.navigate(NavigationEvent.TopLevelTo(MatchingGraph.MatchingRoute)) + navigationHelper.navigate(NavigationEvent.TopLevelTo(MainNavGraphBaseRoute)) } private fun showBottomSheet(content: @Composable () -> Unit) { diff --git a/feature/store/src/main/java/com/puzzle/store/StoreScreen.kt b/feature/store/src/main/java/com/puzzle/store/StoreScreen.kt index ece0e1b7c..ae0f42045 100644 --- a/feature/store/src/main/java/com/puzzle/store/StoreScreen.kt +++ b/feature/store/src/main/java/com/puzzle/store/StoreScreen.kt @@ -24,9 +24,11 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.puzzle.common.ui.LocalSystemBarState import com.puzzle.common.ui.blur import com.puzzle.common.ui.repeatOnStarted import com.puzzle.designsystem.foundation.PieceTheme +import com.puzzle.navigation.TopBarType import com.puzzle.store.contract.StoreIntent import com.puzzle.store.contract.StoreSideEffect import com.puzzle.store.ui.AdaptiveLayout @@ -38,14 +40,37 @@ import com.puzzle.store.ui.page.ExpandedStoreScreen import kotlinx.coroutines.delay @Composable -internal fun StoreRoute(viewModel: StoreViewModel = hiltViewModel()) { +fun StoreRoute( + viewModel: StoreViewModel = hiltViewModel(), + onTopBarConfigChange: (TopBarType) -> Unit = {}, + onBackClick: () -> Unit = {}, +) { val context = LocalContext.current val activity = context as Activity val lifecycleOwner = LocalLifecycleOwner.current val state by viewModel.state.collectAsStateWithLifecycle() + val systemBarState = LocalSystemBarState.current + val lightColor = PieceTheme.colors.light3 var showFullLoading by remember { mutableStateOf(false) } + // Set StatusBar color to light for Store screen + LaunchedEffect(Unit) { + systemBarState.setStatusBarColor(lightColor) + } + + // Update TopBar config for Store + LaunchedEffect(Unit) { + onTopBarConfigChange( + TopBarType.Puzzle( + puzzleCount = 22, // TODO: Get actual puzzle count + showBackButton = true, + onBackClick = onBackClick, + onPuzzleClick = {}, // Store doesn't need puzzle click + onNotificationClick = {} // TODO: Add notification if needed + )) + } + LaunchedEffect(state.isLoading) { if (state.isLoading) { showFullLoading = false @@ -61,8 +86,7 @@ internal fun StoreRoute(viewModel: StoreViewModel = hiltViewModel()) { viewModel.sideEffects.collect { sideEffect -> when (sideEffect) { is StoreSideEffect.PurchaseProduct -> viewModel.billingHelper.purchaseProduct( - activity = activity, - purchaseProduct = sideEffect.product + activity = activity, purchaseProduct = sideEffect.product ) } } @@ -73,19 +97,19 @@ internal fun StoreRoute(viewModel: StoreViewModel = hiltViewModel()) { PurchaseSuccessDialog( purchasedPuzzleCount = state.purchasedPuzzleCount, onDismiss = { viewModel.onIntent(StoreIntent.DismissPurchaseDialog) }, - onHomeClick = { viewModel.onIntent(StoreIntent.OnBackClick) } + onHomeClick = onBackClick ) } if (state.isLoading) { - FullScreenLoading(showSpinner = !showFullLoading){ + FullScreenLoading(showSpinner = !showFullLoading) { AdaptiveLayout( compactContent = { - CompactStoreLoadingScreen { viewModel.onIntent(StoreIntent.OnBackClick) } + CompactStoreLoadingScreen() }, expandedContent = { - ExpandedStoreLoadingScreen { viewModel.onIntent(StoreIntent.OnBackClick) } - } + ExpandedStoreLoadingScreen() + }, ) } } else { @@ -93,7 +117,6 @@ internal fun StoreRoute(viewModel: StoreViewModel = hiltViewModel()) { compactContent = { CompactStoreScreen( state = state, - onBackClick = { viewModel.onIntent(StoreIntent.OnBackClick) }, onInquiryClick = { viewModel.onIntent(StoreIntent.OnInquiryClick) }, onPurchaseClick = { purchaseProduct -> viewModel.onIntent(StoreIntent.OnPurchaseClick(purchaseProduct)) @@ -104,15 +127,13 @@ internal fun StoreRoute(viewModel: StoreViewModel = hiltViewModel()) { expandedContent = { ExpandedStoreScreen( state = state, - onBackClick = { viewModel.onIntent(StoreIntent.OnBackClick) }, onInquiryClick = { viewModel.onIntent(StoreIntent.OnInquiryClick) }, onPurchaseClick = { purchaseProduct -> viewModel.onIntent(StoreIntent.OnPurchaseClick(purchaseProduct)) }, modifier = Modifier.blur(state.isShowPurchaseDialog) ) - } - ) + }) } } diff --git a/feature/store/src/main/java/com/puzzle/store/StoreViewModel.kt b/feature/store/src/main/java/com/puzzle/store/StoreViewModel.kt index f6e9fbdc2..24130eb71 100644 --- a/feature/store/src/main/java/com/puzzle/store/StoreViewModel.kt +++ b/feature/store/src/main/java/com/puzzle/store/StoreViewModel.kt @@ -52,7 +52,6 @@ class StoreViewModel @Inject constructor( override suspend fun processIntent(intent: StoreIntent) { when (intent) { - StoreIntent.OnBackClick -> navigationHelper.navigate(NavigationEvent.Up) StoreIntent.OnInquiryClick -> navigationHelper.navigate( To( SettingGraph.WebViewRoute( diff --git a/feature/store/src/main/java/com/puzzle/store/contract/StoreIntent.kt b/feature/store/src/main/java/com/puzzle/store/contract/StoreIntent.kt index 0fcfaa6b9..388f2ea55 100644 --- a/feature/store/src/main/java/com/puzzle/store/contract/StoreIntent.kt +++ b/feature/store/src/main/java/com/puzzle/store/contract/StoreIntent.kt @@ -4,7 +4,6 @@ import com.puzzle.billing.model.PieceProduct import com.puzzle.common.base.UiIntent sealed class StoreIntent : UiIntent { - data object OnBackClick : StoreIntent() data object OnInquiryClick : StoreIntent() data class OnPurchaseClick(val purchaseProduct: PieceProduct) : StoreIntent() data object DismissPurchaseDialog : StoreIntent() diff --git a/feature/store/src/main/java/com/puzzle/store/navigation/StoreNavigation.kt b/feature/store/src/main/java/com/puzzle/store/navigation/StoreNavigation.kt index 1e5a2033a..b449838ad 100644 --- a/feature/store/src/main/java/com/puzzle/store/navigation/StoreNavigation.kt +++ b/feature/store/src/main/java/com/puzzle/store/navigation/StoreNavigation.kt @@ -2,11 +2,11 @@ package com.puzzle.store.navigation import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable -import com.puzzle.navigation.StoreRoute +import com.puzzle.navigation.MainGraph import com.puzzle.store.StoreRoute fun NavGraphBuilder.storeNavigation() { - composable { + composable { StoreRoute() } } diff --git a/feature/store/src/main/java/com/puzzle/store/ui/components/StoreTopBar.kt b/feature/store/src/main/java/com/puzzle/store/ui/components/StoreTopBar.kt deleted file mode 100644 index 34b7b06dd..000000000 --- a/feature/store/src/main/java/com/puzzle/store/ui/components/StoreTopBar.kt +++ /dev/null @@ -1,89 +0,0 @@ -package com.puzzle.store.ui.components - -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -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.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape -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.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.puzzle.common.ui.clickable -import com.puzzle.designsystem.R -import com.puzzle.designsystem.component.PiecePuzzleTopBar -import com.puzzle.designsystem.foundation.PieceTheme - -@Composable -internal fun StoreTopBar( - count: Int, - onBackClick: () -> Unit, - modifier: Modifier = Modifier -) { - PiecePuzzleTopBar( - count = count, - chipColor = PieceTheme.colors.white, - contentColor = PieceTheme.colors.black, - isShowPlus = false, - leftComponent = { - Image( - painter = painterResource(R.drawable.ic_arrow_left), - contentDescription = "뒤로 가기 버튼", - modifier = Modifier - .padding(end = 8.dp) - .size(32.dp) - .clickable(onClick = onBackClick) - ) - }, - modifier = modifier.fillMaxWidth(), - ) -} - -@Composable -internal fun StoreTopBarLoading(onBackClick: () -> Unit) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp), - modifier = Modifier - .fillMaxWidth() - .height(60.dp) - .background(PieceTheme.colors.light3) - ) { - Image( - painter = painterResource(R.drawable.ic_arrow_left), - contentDescription = "뒤로 가기 버튼", - modifier = Modifier - .size(32.dp) - .clickable { onBackClick() } - ) - - Spacer( - modifier = Modifier - .width(80.dp) - .height(39.dp) - .clip(RoundedCornerShape(24.dp)) - .background(PieceTheme.colors.white) - ) - } -} - -@Preview -@Composable -private fun PreviewStoreTopBar() { - PieceTheme { - StoreTopBar( - count = 5, - onBackClick = { }, - modifier = Modifier.fillMaxWidth() - ) - } -} \ No newline at end of file diff --git a/feature/store/src/main/java/com/puzzle/store/ui/page/CompactStoreLoadingScreen.kt b/feature/store/src/main/java/com/puzzle/store/ui/page/CompactStoreLoadingScreen.kt index 0f4ea3996..26d9e1bd5 100644 --- a/feature/store/src/main/java/com/puzzle/store/ui/page/CompactStoreLoadingScreen.kt +++ b/feature/store/src/main/java/com/puzzle/store/ui/page/CompactStoreLoadingScreen.kt @@ -22,12 +22,9 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.puzzle.designsystem.foundation.PieceTheme -import com.puzzle.store.ui.components.StoreTopBarLoading @Composable -internal fun CompactStoreLoadingScreen( - onBackClick: () -> Unit -) { +internal fun CompactStoreLoadingScreen() { val progress = rememberSkeletonProgress() Column( @@ -37,7 +34,7 @@ internal fun CompactStoreLoadingScreen( .background(PieceTheme.colors.light3) .padding(horizontal = 20.dp) ) { - StoreTopBarLoading(onBackClick) + Spacer(modifier = Modifier.height(20.dp)) val heights = listOf(200.dp, 78.dp, 78.dp, 78.dp, 78.dp) val itemCount = heights.size @@ -78,10 +75,8 @@ private fun rememberSkeletonProgress(): Float { @Preview @Composable -private fun PreviewCompactStoreLoadingScreen(){ +private fun PreviewCompactStoreLoadingScreen() { PieceTheme { - CompactStoreLoadingScreen( - onBackClick = {}, - ) + CompactStoreLoadingScreen() } } diff --git a/feature/store/src/main/java/com/puzzle/store/ui/page/CompactStoreScreen.kt b/feature/store/src/main/java/com/puzzle/store/ui/page/CompactStoreScreen.kt index 008450d7b..c936bd62c 100644 --- a/feature/store/src/main/java/com/puzzle/store/ui/page/CompactStoreScreen.kt +++ b/feature/store/src/main/java/com/puzzle/store/ui/page/CompactStoreScreen.kt @@ -2,7 +2,9 @@ package com.puzzle.store.ui.page import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column +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.runtime.Composable import androidx.compose.ui.Modifier @@ -12,14 +14,12 @@ import com.puzzle.billing.model.PieceProduct import com.puzzle.designsystem.foundation.PieceTheme import com.puzzle.store.contract.StoreState import com.puzzle.store.ui.components.StoreDescription -import com.puzzle.store.ui.components.StoreTopBar import com.puzzle.store.ui.list.NormalProductList import com.puzzle.store.ui.list.PromotionProductList @Composable internal fun CompactStoreScreen( state: StoreState, - onBackClick: () -> Unit, onInquiryClick: () -> Unit, onPurchaseClick: (PieceProduct) -> Unit, modifier: Modifier = Modifier, @@ -30,11 +30,7 @@ internal fun CompactStoreScreen( .background(PieceTheme.colors.light3) .padding(horizontal = 20.dp) ) { - StoreTopBar( - count = state.puzzleCount, - onBackClick = onBackClick, - modifier = modifier.padding(bottom = 20.dp) - ) + Spacer(modifier = Modifier.height(20.dp)) PromotionProductList( promotionProductList = state.promotionProductList, @@ -60,7 +56,6 @@ private fun PreviewCompactStoreScreen() { PieceTheme { CompactStoreScreen( state = StoreState(), - onBackClick = {}, onInquiryClick = {}, onPurchaseClick = {}, ) diff --git a/feature/store/src/main/java/com/puzzle/store/ui/page/ExpandedStoreLoadingScreen.kt b/feature/store/src/main/java/com/puzzle/store/ui/page/ExpandedStoreLoadingScreen.kt index 61a37a734..9ba23bbae 100644 --- a/feature/store/src/main/java/com/puzzle/store/ui/page/ExpandedStoreLoadingScreen.kt +++ b/feature/store/src/main/java/com/puzzle/store/ui/page/ExpandedStoreLoadingScreen.kt @@ -24,22 +24,17 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.puzzle.designsystem.foundation.PieceTheme -import com.puzzle.store.ui.components.StoreTopBarLoading import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList @Composable -internal fun ExpandedStoreLoadingScreen( - onBackClick: () -> Unit -) { +internal fun ExpandedStoreLoadingScreen() { Column( modifier = Modifier .fillMaxSize() .background(PieceTheme.colors.light3) .padding(horizontal = 20.dp) ) { - StoreTopBarLoading(onBackClick) - Spacer(modifier = Modifier.height(20.dp)) Row( @@ -122,6 +117,6 @@ private fun rememberRightListSkeletonProgress(): ImmutableList { @Composable private fun PreviewExpandedStoreLoadingScreen() { PieceTheme { - ExpandedStoreLoadingScreen({}) + ExpandedStoreLoadingScreen() } } \ No newline at end of file diff --git a/feature/store/src/main/java/com/puzzle/store/ui/page/ExpandedStoreScreen.kt b/feature/store/src/main/java/com/puzzle/store/ui/page/ExpandedStoreScreen.kt index 2346ee4c0..4c5da7560 100644 --- a/feature/store/src/main/java/com/puzzle/store/ui/page/ExpandedStoreScreen.kt +++ b/feature/store/src/main/java/com/puzzle/store/ui/page/ExpandedStoreScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable @@ -16,14 +17,12 @@ import com.puzzle.billing.model.PieceProduct import com.puzzle.designsystem.foundation.PieceTheme import com.puzzle.store.contract.StoreState import com.puzzle.store.ui.components.StoreDescription -import com.puzzle.store.ui.components.StoreTopBar import com.puzzle.store.ui.list.NormalProductList import com.puzzle.store.ui.list.PromotionProductList @Composable internal fun ExpandedStoreScreen( state: StoreState, - onBackClick: () -> Unit, onInquiryClick: () -> Unit, onPurchaseClick: (PieceProduct) -> Unit, modifier: Modifier = Modifier, @@ -34,11 +33,7 @@ internal fun ExpandedStoreScreen( .background(PieceTheme.colors.light3) .padding(horizontal = 20.dp) ) { - StoreTopBar( - count = state.puzzleCount, - onBackClick = onBackClick, - modifier = modifier.padding(bottom = 20.dp) - ) + Spacer(modifier = Modifier.height(20.dp)) Row(modifier = Modifier.fillMaxSize()) { PromotionProductList( @@ -77,7 +72,6 @@ private fun PreviewExpandedStoreScreen() { PieceTheme { ExpandedStoreScreen( state = StoreState(), - onBackClick = {}, onInquiryClick = {}, onPurchaseClick = {}, ) diff --git a/gradle.properties b/gradle.properties index 8c4672e91..a1403aeb1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx16384m -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. For more details, visit # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects diff --git a/presentation/src/main/java/com/puzzle/presentation/MainActivity.kt b/presentation/src/main/java/com/puzzle/presentation/MainActivity.kt index 9b5c939f2..f2da9f36f 100644 --- a/presentation/src/main/java/com/puzzle/presentation/MainActivity.kt +++ b/presentation/src/main/java/com/puzzle/presentation/MainActivity.kt @@ -34,7 +34,7 @@ import com.puzzle.designsystem.R import com.puzzle.designsystem.foundation.PieceTheme import com.puzzle.domain.model.user.UserRole import com.puzzle.navigation.AuthGraphBaseRoute -import com.puzzle.navigation.MatchingGraph +import com.puzzle.navigation.MainNavGraphBaseRoute import com.puzzle.navigation.NavigationEvent import com.puzzle.navigation.NavigationEvent.BottomBarTo import com.puzzle.navigation.NavigationEvent.To @@ -153,7 +153,7 @@ class MainActivity : ComponentActivity() { private fun handleNotificationIntent(intent: Intent?) { intent?.getStringExtra(NOTIFCATION_ID)?.let { id -> viewModel.readNotification(id.toIntOrNull() ?: return@let) - navigationHelper.navigate(To(MatchingGraph.MatchingRoute)) + navigationHelper.navigate(To(MainNavGraphBaseRoute)) // Todo : 서버 Fcm Data 스펙 변경되면 Notification Type이 추가되면 그대로 유지, 아니라면 navigation 로직 위로 올려야 함 // intent?.getStringExtra(NOTIFCATION_TYPE)?.let { type -> @@ -226,7 +226,7 @@ class MainActivity : ComponentActivity() { is BottomBarTo -> { val topLevelNavOptions = navOptions { - popUpTo(MatchingGraph.MatchingRoute) { saveState = true } + popUpTo(MainNavGraphBaseRoute) { saveState = true } launchSingleTop = true restoreState = true } diff --git a/presentation/src/main/java/com/puzzle/presentation/MainViewModel.kt b/presentation/src/main/java/com/puzzle/presentation/MainViewModel.kt index 777a92d9f..632b7610c 100644 --- a/presentation/src/main/java/com/puzzle/presentation/MainViewModel.kt +++ b/presentation/src/main/java/com/puzzle/presentation/MainViewModel.kt @@ -22,7 +22,7 @@ import com.puzzle.domain.repository.ProfileRepository import com.puzzle.domain.repository.TermsRepository import com.puzzle.domain.repository.UserRepository import com.puzzle.navigation.AuthGraphBaseRoute -import com.puzzle.navigation.MatchingGraph +import com.puzzle.navigation.MainNavGraphBaseRoute import com.puzzle.navigation.NavigationEvent.TopLevelTo import com.puzzle.navigation.NavigationHelper import com.puzzle.navigation.OnboardingRoute @@ -154,7 +154,7 @@ class MainViewModel @Inject constructor( errorHelper.setUserId(userInfo.userId.toString()) when (userInfo.userRole) { - PENDING, USER -> navigationHelper.navigate(TopLevelTo(route = MatchingGraph.MatchingRoute)) + PENDING, USER -> navigationHelper.navigate(TopLevelTo(route = MainNavGraphBaseRoute)) else -> Unit } } diff --git a/presentation/src/main/java/com/puzzle/presentation/navigation/AppBottomBar.kt b/presentation/src/main/java/com/puzzle/presentation/navigation/AppBottomBar.kt index 0bca216bd..66c4acb3e 100644 --- a/presentation/src/main/java/com/puzzle/presentation/navigation/AppBottomBar.kt +++ b/presentation/src/main/java/com/puzzle/presentation/navigation/AppBottomBar.kt @@ -21,7 +21,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.puzzle.common.ui.NoRippleInteractionSource import com.puzzle.designsystem.foundation.PieceTheme -import com.puzzle.navigation.MatchingGraphBaseRoute +import com.puzzle.navigation.MainNavGraphBaseRoute import com.puzzle.navigation.ProfileGraph import com.puzzle.navigation.Route import com.puzzle.navigation.SettingGraphBaseRoute @@ -94,7 +94,7 @@ internal fun AppBottomBar( onClick = { when (topLevelRoute) { TopLevelDestination.MATCHING -> - navigateToBottomNaviDestination(MatchingGraphBaseRoute) + navigateToBottomNaviDestination(MainNavGraphBaseRoute) TopLevelDestination.PROFILE -> navigateToBottomNaviDestination(ProfileGraph.MainProfileRoute) diff --git a/presentation/src/main/java/com/puzzle/presentation/navigation/AppNavHost.kt b/presentation/src/main/java/com/puzzle/presentation/navigation/AppNavHost.kt index 983ee5166..5eddd7b7d 100644 --- a/presentation/src/main/java/com/puzzle/presentation/navigation/AppNavHost.kt +++ b/presentation/src/main/java/com/puzzle/presentation/navigation/AppNavHost.kt @@ -8,13 +8,11 @@ import com.puzzle.auth.navigation.authNavGraph import com.puzzle.common.ui.pieceEnterTransitionAnimation import com.puzzle.common.ui.pieceExitTransitionAnimation import com.puzzle.debug.analyticsNavigation -import com.puzzle.matching.navigation.matchingNavGraph import com.puzzle.navigation.AuthGraphBaseRoute import com.puzzle.notification.navigation.notificationNavigation import com.puzzle.onboarding.navigation.onboardingNavigation import com.puzzle.profile.navigation.profileNavGraph import com.puzzle.setting.navigation.settingNavGraph -import com.puzzle.store.navigation.storeNavigation @Composable fun AppNavHost( @@ -30,11 +28,10 @@ fun AppNavHost( ) { onboardingNavigation() authNavGraph() - matchingNavGraph() + matchingStoreNavGraph() profileNavGraph() settingNavGraph() notificationNavigation() - storeNavigation() analyticsNavigation() } } diff --git a/presentation/src/main/java/com/puzzle/presentation/navigation/MainNavGraph.kt b/presentation/src/main/java/com/puzzle/presentation/navigation/MainNavGraph.kt new file mode 100644 index 000000000..7ed31d259 --- /dev/null +++ b/presentation/src/main/java/com/puzzle/presentation/navigation/MainNavGraph.kt @@ -0,0 +1,225 @@ +package com.puzzle.presentation.navigation + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Scaffold +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.graphics.ColorFilter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.navigation +import androidx.navigation.compose.rememberNavController +import com.puzzle.designsystem.R +import com.puzzle.designsystem.component.PieceMainTopBar +import com.puzzle.designsystem.component.PiecePuzzleChip +import com.puzzle.designsystem.foundation.PieceTheme +import com.puzzle.matching.graph.block.BlockRoute +import com.puzzle.matching.graph.contact.ContactRoute +import com.puzzle.matching.graph.detail.MatchingDetailRoute +import com.puzzle.matching.graph.main.MatchingRoute +import com.puzzle.matching.graph.preview.ProfilePreviewRoute +import com.puzzle.matching.graph.report.ReportRoute +import com.puzzle.navigation.MainGraph +import com.puzzle.navigation.MainNavGraphBaseRoute +import com.puzzle.navigation.MatchingGraph +import com.puzzle.navigation.TopBarType +import com.puzzle.store.StoreRoute as StoreScreenRoute + +fun NavGraphBuilder.matchingStoreNavGraph() { + composable { + MainNavScreen() + } +} + +@Composable +private fun MainNavScreen() { + val navController = rememberNavController() + var topBarConfig by remember { mutableStateOf(TopBarType.None) } + + Scaffold( + topBar = { + when (val config = topBarConfig) { + is TopBarType.None -> Unit + is TopBarType.Main -> { + Box( + modifier = Modifier + .fillMaxWidth() + .height(60.dp) + .background(PieceTheme.colors.black) + ) { + PieceMainTopBar( + title = "매칭", + textStyle = PieceTheme.typography.branding, + titleColor = PieceTheme.colors.white, + modifier = Modifier.padding(horizontal = 20.dp), + rightComponent = { + Image( + painter = painterResource(com.puzzle.designsystem.R.drawable.ic_alarm_black), + contentDescription = "알람", + colorFilter = ColorFilter.tint(PieceTheme.colors.white), + modifier = Modifier + .size(32.dp) + .clickable { config.onNotificationClick() } + ) + } + ) + } + } + + is TopBarType.Puzzle -> { + val backgroundColor = + if (config.showBackButton) PieceTheme.colors.light3 else PieceTheme.colors.black + val contentColor = + if (config.showBackButton) PieceTheme.colors.black else PieceTheme.colors.white + + // Animate PuzzleChip offset based on back button visibility + val puzzleChipOffset by animateDpAsState( + targetValue = if (config.showBackButton) 40.dp else 0.dp, + animationSpec = tween(700), + label = "puzzleChipOffset" + ) + + // Animate PuzzleChip background color + val chipColor by animateColorAsState( + targetValue = if (config.showBackButton) + PieceTheme.colors.white + else + PieceTheme.colors.white.copy(alpha = 0.1f), + animationSpec = tween(700), + label = "chipColor" + ) + + // Animate PuzzleChip content color + val animatedContentColor by animateColorAsState( + targetValue = contentColor, + animationSpec = tween(700), + label = "contentColor" + ) + + Box( + modifier = Modifier + .fillMaxWidth() + .height(60.dp) + .background(backgroundColor) + .padding(horizontal = 20.dp) + ) { + AnimatedVisibility( + visible = config.showBackButton, + enter = fadeIn(animationSpec = tween(700)), + exit = fadeOut(animationSpec = tween(700)), + modifier = Modifier.align(Alignment.CenterStart), + ) { + Image( + painter = painterResource(R.drawable.ic_arrow_left), + contentDescription = "뒤로가기", + colorFilter = ColorFilter.tint(contentColor), + modifier = Modifier + .size(32.dp) + .clickable { config.onBackClick() } + ) + } + + PiecePuzzleChip( + count = config.puzzleCount, + isShowPlus = !config.showBackButton, + chipColor = chipColor, + contentColor = animatedContentColor, + onChipClick = config.onPuzzleClick, + modifier = Modifier + .align(Alignment.CenterStart) + .offset(x = puzzleChipOffset), + ) + + Image( + painter = painterResource(com.puzzle.designsystem.R.drawable.ic_alarm_black), + contentDescription = "알람", + colorFilter = ColorFilter.tint(contentColor), + modifier = Modifier + .align(Alignment.CenterEnd) + .size(32.dp) + .clickable { config.onNotificationClick() } + ) + } + } + } + } + ) { innerPadding -> + NavHost( + navController = navController, + startDestination = MainGraph.MatchingGraphBaseRoute, + modifier = Modifier + .fillMaxSize() + .padding(top = innerPadding.calculateTopPadding()) + ) { + // Matching nested navigation + navigation( + startDestination = MatchingGraph.MatchingRoute + ) { + composable { + MatchingRoute( + onTopBarConfigChange = { config -> + topBarConfig = config + }, + onNavigateToStore = { + navController.navigate(MainGraph.StoreRoute) + } + ) + } + + composable { + MatchingDetailRoute() + } + + composable { + ReportRoute() + } + + composable { + BlockRoute() + } + + composable { + ContactRoute() + } + + composable { + ProfilePreviewRoute() + } + } + + composable { + StoreScreenRoute( + onTopBarConfigChange = { config -> + topBarConfig = config + }, + onBackClick = { + navController.popBackStack() + } + ) + } + } + } +} diff --git a/presentation/src/main/java/com/puzzle/presentation/navigation/TopLevelDestinvation.kt b/presentation/src/main/java/com/puzzle/presentation/navigation/TopLevelDestinvation.kt index 04f3ecb12..8b11c32fe 100644 --- a/presentation/src/main/java/com/puzzle/presentation/navigation/TopLevelDestinvation.kt +++ b/presentation/src/main/java/com/puzzle/presentation/navigation/TopLevelDestinvation.kt @@ -2,7 +2,7 @@ package com.puzzle.presentation.navigation import androidx.annotation.DrawableRes import com.puzzle.designsystem.R -import com.puzzle.navigation.MatchingGraph +import com.puzzle.navigation.MainNavGraphBaseRoute import com.puzzle.navigation.ProfileGraph import com.puzzle.navigation.SettingGraphBaseRoute import kotlin.reflect.KClass @@ -23,7 +23,7 @@ enum class TopLevelDestination( iconDrawableId = R.drawable.ic_profile, contentDescription = "매칭", title = "", - route = MatchingGraph.MatchingRoute::class, + route = MainNavGraphBaseRoute::class, ), SETTING( iconDrawableId = R.drawable.ic_setting, diff --git a/presentation/src/main/java/com/puzzle/presentation/ui/AppState.kt b/presentation/src/main/java/com/puzzle/presentation/ui/AppState.kt index aef6ef62a..986bbf058 100644 --- a/presentation/src/main/java/com/puzzle/presentation/ui/AppState.kt +++ b/presentation/src/main/java/com/puzzle/presentation/ui/AppState.kt @@ -65,8 +65,8 @@ class PieceAppState( val statusBarColor: Color @Composable get() = when { shouldFullScreen -> Color.Transparent - currentDestination.startsWithAnyRouteIn(AppUiPolicy.blackStatusBarRoutes) -> PieceTheme.colors.black currentDestination.startsWithAnyRouteIn(AppUiPolicy.lightStatusBarRoutes) -> PieceTheme.colors.light3 + currentDestination.startsWithAnyRouteIn(AppUiPolicy.blackStatusBarRoutes) -> PieceTheme.colors.black else -> PieceTheme.colors.white } diff --git a/presentation/src/main/java/com/puzzle/presentation/ui/AppUiPolicy.kt b/presentation/src/main/java/com/puzzle/presentation/ui/AppUiPolicy.kt index fe627a515..37435b9d1 100644 --- a/presentation/src/main/java/com/puzzle/presentation/ui/AppUiPolicy.kt +++ b/presentation/src/main/java/com/puzzle/presentation/ui/AppUiPolicy.kt @@ -2,13 +2,14 @@ package com.puzzle.presentation.ui import com.puzzle.navigation.AnalyticsRoute import com.puzzle.navigation.AuthGraphBaseRoute +import com.puzzle.navigation.MainGraph +import com.puzzle.navigation.MainNavGraphBaseRoute import com.puzzle.navigation.MatchingGraph import com.puzzle.navigation.NotificationRoute import com.puzzle.navigation.OnboardingRoute import com.puzzle.navigation.PauseRoute import com.puzzle.navigation.ProfileGraph import com.puzzle.navigation.SettingGraph -import com.puzzle.navigation.StoreRoute object AppUiPolicy { val bottomBarHiddenRoutes = setOf( @@ -27,7 +28,7 @@ object AppUiPolicy { SettingGraph.WithdrawRoute::class, SettingGraph.WebViewRoute::class, NotificationRoute::class, - StoreRoute::class, + MainGraph.StoreRoute::class, AnalyticsRoute::class, ) @@ -38,14 +39,16 @@ object AppUiPolicy { ) val blackStatusBarRoutes = setOf( + MainNavGraphBaseRoute::class, + MainGraph.MatchingGraphBaseRoute::class, MatchingGraph.MatchingRoute::class, ) val lightStatusBarRoutes = setOf( - StoreRoute::class, + MainGraph.StoreRoute::class, ) val lightNavigationBarRoutes = setOf( - StoreRoute::class, + MainGraph.StoreRoute::class, ) } diff --git a/presentation/src/main/java/com/puzzle/presentation/ui/PieceApp.kt b/presentation/src/main/java/com/puzzle/presentation/ui/PieceApp.kt index 5ee542540..5e9066d0b 100644 --- a/presentation/src/main/java/com/puzzle/presentation/ui/PieceApp.kt +++ b/presentation/src/main/java/com/puzzle/presentation/ui/PieceApp.kt @@ -37,7 +37,7 @@ import com.puzzle.designsystem.component.PieceSnackBarHost import com.puzzle.designsystem.component.PieceTopShadowFab import com.puzzle.designsystem.foundation.PieceTheme import com.puzzle.domain.model.configure.ForceUpdate -import com.puzzle.navigation.MatchingGraphBaseRoute +import com.puzzle.navigation.MainNavGraphBaseRoute import com.puzzle.navigation.Route import com.puzzle.presentation.navigation.AppBottomBar import com.puzzle.presentation.navigation.AppNavHost @@ -78,7 +78,7 @@ fun PieceApp( floatingActionButton = { if (!appState.shouldHideBottomBar) { PieceTopShadowFab( - onClick = { navigateToBottomNaviDestination(MatchingGraphBaseRoute) }, + onClick = { navigateToBottomNaviDestination(MainNavGraphBaseRoute) }, iconResId = R.drawable.ic_matching, ) } From f6a314179acd5face30709402312cef2b70de203 Mon Sep 17 00:00:00 2001 From: tgyuuAn Date: Sat, 22 Nov 2025 19:03:05 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[AND-1395]=20Matching=20<->=20Store=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20TopBar=20=EC=95=A0=EB=8B=88=EB=A9=94?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80=ED=95=B4=EC=9A=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/puzzle/common/ui/SystemBarState.kt | 7 +++++++ .../com/puzzle/matching/graph/main/MatchingScreen.kt | 1 + .../src/main/java/com/puzzle/store/StoreScreen.kt | 1 + .../com/puzzle/store/navigation/StoreNavigation.kt | 12 ------------ .../main/java/com/puzzle/presentation/ui/PieceApp.kt | 7 +++++-- 5 files changed, 14 insertions(+), 14 deletions(-) delete mode 100644 feature/store/src/main/java/com/puzzle/store/navigation/StoreNavigation.kt diff --git a/core/common-ui/src/main/java/com/puzzle/common/ui/SystemBarState.kt b/core/common-ui/src/main/java/com/puzzle/common/ui/SystemBarState.kt index 9c8d4a30b..7a53d272d 100644 --- a/core/common-ui/src/main/java/com/puzzle/common/ui/SystemBarState.kt +++ b/core/common-ui/src/main/java/com/puzzle/common/ui/SystemBarState.kt @@ -21,6 +21,9 @@ class SystemBarState { private val _navigationBarColor = MutableStateFlow(Color.Transparent) val navigationBarColor: StateFlow = _navigationBarColor + private val _shouldHideBottomBar = MutableStateFlow(false) + val shouldHideBottomBar: StateFlow = _shouldHideBottomBar.asStateFlow() + fun setStatusBarColor(color: Color) { _statusBarColor.value = color } @@ -28,6 +31,10 @@ class SystemBarState { fun setNavigationBarColor(color: Color) { _navigationBarColor.value = color } + + fun setBottomBarVisibility(shouldHide: Boolean) { + _shouldHideBottomBar.value = shouldHide + } } val LocalSystemBarState = compositionLocalOf { diff --git a/feature/matching/src/main/java/com/puzzle/matching/graph/main/MatchingScreen.kt b/feature/matching/src/main/java/com/puzzle/matching/graph/main/MatchingScreen.kt index cc3708fb0..dbe4ad950 100644 --- a/feature/matching/src/main/java/com/puzzle/matching/graph/main/MatchingScreen.kt +++ b/feature/matching/src/main/java/com/puzzle/matching/graph/main/MatchingScreen.kt @@ -55,6 +55,7 @@ fun MatchingRoute( // Set StatusBar color to black for Matching screens LaunchedEffect(Unit) { systemBarState.setStatusBarColor(blackColor) + systemBarState.setBottomBarVisibility(shouldHide = false) } // Update TopBar config based on matching status diff --git a/feature/store/src/main/java/com/puzzle/store/StoreScreen.kt b/feature/store/src/main/java/com/puzzle/store/StoreScreen.kt index ae0f42045..2360d2d63 100644 --- a/feature/store/src/main/java/com/puzzle/store/StoreScreen.kt +++ b/feature/store/src/main/java/com/puzzle/store/StoreScreen.kt @@ -57,6 +57,7 @@ fun StoreRoute( // Set StatusBar color to light for Store screen LaunchedEffect(Unit) { systemBarState.setStatusBarColor(lightColor) + systemBarState.setBottomBarVisibility(shouldHide = true) } // Update TopBar config for Store diff --git a/feature/store/src/main/java/com/puzzle/store/navigation/StoreNavigation.kt b/feature/store/src/main/java/com/puzzle/store/navigation/StoreNavigation.kt deleted file mode 100644 index b449838ad..000000000 --- a/feature/store/src/main/java/com/puzzle/store/navigation/StoreNavigation.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.puzzle.store.navigation - -import androidx.navigation.NavGraphBuilder -import androidx.navigation.compose.composable -import com.puzzle.navigation.MainGraph -import com.puzzle.store.StoreRoute - -fun NavGraphBuilder.storeNavigation() { - composable { - StoreRoute() - } -} diff --git a/presentation/src/main/java/com/puzzle/presentation/ui/PieceApp.kt b/presentation/src/main/java/com/puzzle/presentation/ui/PieceApp.kt index 5e9066d0b..7b39e2c2c 100644 --- a/presentation/src/main/java/com/puzzle/presentation/ui/PieceApp.kt +++ b/presentation/src/main/java/com/puzzle/presentation/ui/PieceApp.kt @@ -53,6 +53,9 @@ fun PieceApp( navigateToBottomNaviDestination: (Route) -> Unit, ) { val scope = rememberCoroutineScope() + val systemBarState = LocalSystemBarState.current + val systemBarShouldHideBottomBar by systemBarState.shouldHideBottomBar.collectAsStateWithLifecycle() + val shouldHideBottomBar = appState.shouldHideBottomBar || systemBarShouldHideBottomBar DebugDrawer(navController = appState.navController) { PieceModalBottomSheet(bottomSheetState = appState.bottomSheetState) { @@ -66,7 +69,7 @@ fun PieceApp( containerColor = PieceTheme.colors.white, bottomBar = { PieceBottomBarAnimation( - visible = !appState.shouldHideBottomBar, + visible = !shouldHideBottomBar, modifier = Modifier.navigationBarsPadding(), ) { AppBottomBar( @@ -76,7 +79,7 @@ fun PieceApp( } }, floatingActionButton = { - if (!appState.shouldHideBottomBar) { + if (!shouldHideBottomBar) { PieceTopShadowFab( onClick = { navigateToBottomNaviDestination(MainNavGraphBaseRoute) }, iconResId = R.drawable.ic_matching,