diff --git a/core/designsystem/src/main/res/drawable/img_card_empty.png b/core/designsystem/src/main/res/drawable/img_card_empty.png new file mode 100644 index 00000000..57fa91e6 Binary files /dev/null and b/core/designsystem/src/main/res/drawable/img_card_empty.png differ diff --git a/core/designsystem/src/main/res/drawable/img_join_card.png b/core/designsystem/src/main/res/drawable/img_join_card.png new file mode 100644 index 00000000..4f81aa86 Binary files /dev/null and b/core/designsystem/src/main/res/drawable/img_join_card.png differ diff --git a/core/network/src/main/java/com/plottwist/core/network/interceptor/TokenInterceptor.kt b/core/network/src/main/java/com/plottwist/core/network/interceptor/TokenInterceptor.kt index f67a607b..f3fdc197 100644 --- a/core/network/src/main/java/com/plottwist/core/network/interceptor/TokenInterceptor.kt +++ b/core/network/src/main/java/com/plottwist/core/network/interceptor/TokenInterceptor.kt @@ -44,16 +44,25 @@ class TokenInterceptor @Inject constructor( HttpURLConnection.HTTP_UNAUTHORIZED -> { val retryRequest = chain.request().newBuilder().apply { runBlocking { - tokenProvider.getRefreshToken()?.let { - val newToken = authApiService.refreshToken(TokenRequest(it)) - if (newToken.success) { - newToken.data.let { token -> - addHeader("Authorization", "Bearer ${token.accessToken}") - CoroutineScope(Dispatchers.IO).launch { - tokenProvider.setAccessToken(token.accessToken) - tokenProvider.setRefreshToken(token.refreshToken) + val accessToken = tokenProvider.getAccessToken() + val refreshToken = tokenProvider.getRefreshToken() + if(accessToken.isNullOrEmpty() && refreshToken.isNullOrEmpty()){ + return@runBlocking + } + refreshToken?.let { + try { + val newToken = authApiService.refreshToken(TokenRequest(it)) + if (newToken.success) { + newToken.data.let { token -> + addHeader("Authorization", "Bearer ${token.accessToken}") + CoroutineScope(Dispatchers.IO).launch { + tokenProvider.setAccessToken(token.accessToken) + tokenProvider.setRefreshToken(token.refreshToken) + } } } + } catch (e: Exception) { + return@let } } } diff --git a/feature/join_gathering/src/main/java/com/plottwist/join_gathering/JoinGatheringContract.kt b/feature/join_gathering/src/main/java/com/plottwist/join_gathering/JoinGatheringContract.kt index 76160f06..9762fc65 100644 --- a/feature/join_gathering/src/main/java/com/plottwist/join_gathering/JoinGatheringContract.kt +++ b/feature/join_gathering/src/main/java/com/plottwist/join_gathering/JoinGatheringContract.kt @@ -12,4 +12,5 @@ sealed class JoinGatheringAction { sealed class JoinGatheringSideEffect { data class NavigateToGatheringDetail(val gatheringId: Long) : JoinGatheringSideEffect() data class ShowSnackbar(val message: String) : JoinGatheringSideEffect() + data object NavigateToLoginScreen :JoinGatheringSideEffect() } diff --git a/feature/join_gathering/src/main/java/com/plottwist/join_gathering/JoinGatheringScreen.kt b/feature/join_gathering/src/main/java/com/plottwist/join_gathering/JoinGatheringScreen.kt index 4b960822..0bd81fe6 100644 --- a/feature/join_gathering/src/main/java/com/plottwist/join_gathering/JoinGatheringScreen.kt +++ b/feature/join_gathering/src/main/java/com/plottwist/join_gathering/JoinGatheringScreen.kt @@ -1,21 +1,22 @@ package com.plottwist.join_gathering -import androidx.compose.foundation.layout.Arrangement +import android.annotation.SuppressLint import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredWidth import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState @@ -27,35 +28,38 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.BlurredEdgeTreatment +import androidx.compose.ui.draw.blur +import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.clipRect import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.plottwist.core.designsystem.R import com.plottwist.core.designsystem.component.TukTopAppBar -import com.plottwist.core.designsystem.foundation.TukColorTokens.Gray300 -import com.plottwist.core.designsystem.foundation.TukColorTokens.Gray500 -import com.plottwist.core.designsystem.foundation.TukColorTokens.Gray700 import com.plottwist.core.designsystem.foundation.TukPrimitivesColor import com.plottwist.core.designsystem.foundation.type.TukSerifTypography import com.plottwist.core.ui.component.StableImage -import com.plottwist.core.ui.component.TukScaffold +import com.plottwist.core.ui.component.TukScaffoldTitle import kotlinx.coroutines.launch +@SuppressLint("ConfigurationScreenWidthHeight") @Composable fun JoinGatheringScreen( onCloseClicked: () -> Unit = {}, onNavigateToGatheringDetail: (Long) -> Unit, + onNavigateToLoginScreen: () -> Unit, viewModel: JoinGatheringViewModel = hiltViewModel() ) { val state by viewModel.container.stateFlow.collectAsStateWithLifecycle() val snackbarHostState = remember { SnackbarHostState() } val coroutineScope = rememberCoroutineScope() + val localConfiguration = LocalConfiguration.current LaunchedEffect(Unit) { viewModel.container.sideEffectFlow.collect { sideEffect -> @@ -71,6 +75,10 @@ fun JoinGatheringScreen( ) } } + + JoinGatheringSideEffect.NavigateToLoginScreen -> { + onNavigateToLoginScreen() + } } } @@ -79,113 +87,89 @@ fun JoinGatheringScreen( Box( modifier = Modifier ) { + StableImage( modifier = Modifier - .fillMaxWidth() - .align(Alignment.BottomEnd), - drawableResId = R.drawable.image_join_gathering_gradient, + .fillMaxWidth() .requiredWidth( + localConfiguration.screenWidthDp.dp * GRADIENT_BACKGROUND_IMAGE_SCALE + ), + drawableResId = R.drawable.image_home_gradient, contentScale = ContentScale.FillWidth ) - TukScaffold ( - containerColor = Color.Transparent, - snackbarHost = { - SnackbarHost( - hostState = snackbarHostState - ) - }, + Scaffold( + modifier = Modifier + .fillMaxSize() + .imePadding(), topBar = { JoinGatheringAppBar( onCloseClicked = onCloseClicked ) }, - title = "모임에\n참여하시겠어요?", bottomBar = { JoinGatheringButton( onClick = { viewModel.handleAction(JoinGatheringAction.ClickJoin) } ) - } - ) { - item { + }, + snackbarHost = { + SnackbarHost( + hostState = snackbarHostState + ) + }, + containerColor = Color.Transparent + ) { innerPadding -> + Column ( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding) + ) { + TukScaffoldTitle( + title = "모임에\n참여하시겠어요?", + modifier = Modifier.padding(horizontal = 20.dp) + ) JoinGatheringContent( modifier = Modifier - .fillMaxWidth() - .padding(vertical = 60.dp), - gatheringName = state.gatheringName + .fillMaxWidth(), + gatheringName = state.gatheringName, + screenWidth = localConfiguration.screenWidthDp.toFloat(), + screenHeight = localConfiguration.screenHeightDp.toFloat() ) } } - } } @Composable fun JoinGatheringContent( gatheringName: String, + screenWidth : Float, + screenHeight: Float, modifier: Modifier ) { Box( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(288f / 349f), + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center ) { - StableImage( - modifier = Modifier - .fillMaxSize() - .padding(horizontal = 10.dp), - drawableResId = R.drawable.image_join_gathering_card - ) - Box( - modifier = Modifier + Modifier .fillMaxWidth() - .padding(24.dp) ) { StableImage( - modifier = Modifier.fillMaxSize(), - drawableResId = R.drawable.image_join_gathering_card, - contentScale = ContentScale.FillBounds - ) - - Column( modifier = Modifier - .fillMaxSize() - .padding(horizontal = 24.dp, vertical = 36.dp), - verticalArrangement = Arrangement.SpaceBetween - ) { - Box( - modifier = Modifier - .fillMaxWidth() - .padding(top = 63.dp), - contentAlignment = Alignment.Center - ) { - Text( - text = gatheringName, - textAlign = TextAlign.Center, - style = TukSerifTypography.title22M - ) - } + .fillMaxWidth() + .aspectRatio(260f/364f), + drawableResId = R.drawable.img_join_card, + contentScale = ContentScale.FillWidth + ) - Row( - modifier = Modifier - .fillMaxWidth() - .padding(start = 38.dp, end = 38.dp, bottom = 33.dp), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text( - text = "연락이", - style = TukSerifTypography.body14R, - color = Gray700 - ) - Text( - text = "뜸해진 우리", - style = TukSerifTypography.body14R, - color = Gray700 - ) - } - } + Text( + modifier = Modifier.align(Alignment.Center), + text = "$gatheringName\n친구들에게", + style = TukSerifTypography.title18M, + textAlign = TextAlign.Center + ) } } } @@ -244,3 +228,14 @@ fun TopAppBarCloseButton( ) } } + +private const val GRADIENT_BACKGROUND_IMAGE_SCALE = 3 + +@Preview +@Composable +private fun JoinGatheringScreenPreview() { + JoinGatheringScreen( + onNavigateToGatheringDetail = {}, + onNavigateToLoginScreen = {} + ) +} diff --git a/feature/join_gathering/src/main/java/com/plottwist/join_gathering/JoinGatheringViewModel.kt b/feature/join_gathering/src/main/java/com/plottwist/join_gathering/JoinGatheringViewModel.kt index 20bcb724..ffe04688 100644 --- a/feature/join_gathering/src/main/java/com/plottwist/join_gathering/JoinGatheringViewModel.kt +++ b/feature/join_gathering/src/main/java/com/plottwist/join_gathering/JoinGatheringViewModel.kt @@ -1,7 +1,5 @@ package com.plottwist.join_gathering -import android.net.http.HttpException -import android.util.Log import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.navigation.toRoute @@ -55,7 +53,11 @@ class JoinGatheringViewModel @Inject constructor( postSideEffect(JoinGatheringSideEffect.NavigateToGatheringDetail(state.gatheringId)) }, onFailure = { - postSideEffect(JoinGatheringSideEffect.ShowSnackbar("모임에 입장할 수 없습니다.")) + if(it.message?.contains("401") == true){ + postSideEffect(JoinGatheringSideEffect.NavigateToLoginScreen) + }else { + postSideEffect(JoinGatheringSideEffect.ShowSnackbar("모임에 입장할 수 없습니다.")) + } } ) } diff --git a/feature/join_gathering/src/main/java/com/plottwist/join_gathering/navigation/JoinGatheringNavigation.kt b/feature/join_gathering/src/main/java/com/plottwist/join_gathering/navigation/JoinGatheringNavigation.kt index 142a7ad9..986c7f9f 100644 --- a/feature/join_gathering/src/main/java/com/plottwist/join_gathering/navigation/JoinGatheringNavigation.kt +++ b/feature/join_gathering/src/main/java/com/plottwist/join_gathering/navigation/JoinGatheringNavigation.kt @@ -24,6 +24,7 @@ fun NavController.navigateToJoinGathering( fun NavGraphBuilder.createJoinGatheringNavGraph( onBack: () -> Unit, onNavigateToGatheringDetail : (Long) -> Unit, + onNavigateToLoginScreen: () -> Unit ) { composable ( deepLinks = listOf( @@ -41,7 +42,8 @@ fun NavGraphBuilder.createJoinGatheringNavGraph( ) { JoinGatheringScreen( onCloseClicked = onBack, - onNavigateToGatheringDetail = onNavigateToGatheringDetail + onNavigateToGatheringDetail = onNavigateToGatheringDetail, + onNavigateToLoginScreen = onNavigateToLoginScreen ) } } diff --git a/feature/main/src/main/java/com/plottwist/feature/main/ui/component/TukNavHost.kt b/feature/main/src/main/java/com/plottwist/feature/main/ui/component/TukNavHost.kt index bbb29b42..46b38efe 100644 --- a/feature/main/src/main/java/com/plottwist/feature/main/ui/component/TukNavHost.kt +++ b/feature/main/src/main/java/com/plottwist/feature/main/ui/component/TukNavHost.kt @@ -108,6 +108,7 @@ fun TukNavHost( navController.popBackStack() }, navigateToOnboarding = { + navController.popBackStack() navController.navigateToOnboardingName() } ) @@ -198,7 +199,7 @@ fun TukNavHost( ) onboardingNameNavGraph( onBack = { - + navController.popBackStack() }, navigateToHomeScreen = { navController.popBackStack(Route.Home, inclusive = false) @@ -246,6 +247,9 @@ fun TukNavHost( } } ) + }, + onNavigateToLoginScreen = { + navController.navigateToLogin() } ) completeProposalNavGraph( diff --git a/feature/onboarding/src/main/java/com/plottwist/feature/onboarding/OnboardingNameContract.kt b/feature/onboarding/src/main/java/com/plottwist/feature/onboarding/OnboardingNameContract.kt index 5ad5c165..3a07f39d 100644 --- a/feature/onboarding/src/main/java/com/plottwist/feature/onboarding/OnboardingNameContract.kt +++ b/feature/onboarding/src/main/java/com/plottwist/feature/onboarding/OnboardingNameContract.kt @@ -12,5 +12,5 @@ sealed class OnboardingNameAction { } sealed class OnboardingNameSideEffect { - data object NavigateToHomeScreen : OnboardingNameSideEffect() + data object NavigateToBack : OnboardingNameSideEffect() } diff --git a/feature/onboarding/src/main/java/com/plottwist/feature/onboarding/OnboardingNameScreen.kt b/feature/onboarding/src/main/java/com/plottwist/feature/onboarding/OnboardingNameScreen.kt index b507379e..23ff0f97 100644 --- a/feature/onboarding/src/main/java/com/plottwist/feature/onboarding/OnboardingNameScreen.kt +++ b/feature/onboarding/src/main/java/com/plottwist/feature/onboarding/OnboardingNameScreen.kt @@ -20,7 +20,6 @@ import com.plottwist.core.designsystem.component.TukSolidButton import com.plottwist.core.designsystem.component.TukSolidButtonType import com.plottwist.core.designsystem.component.TukTextField import com.plottwist.core.designsystem.component.TukTopAppBar -import com.plottwist.core.ui.component.TopAppBarCloseButton import com.plottwist.core.ui.component.TukScaffold import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect @@ -38,9 +37,9 @@ fun OnboardingNameScreen( viewModel.collectSideEffect { sideEffect -> when (sideEffect) { - OnboardingNameSideEffect.NavigateToHomeScreen -> { + OnboardingNameSideEffect.NavigateToBack -> { keyboardController?.hide() - navigateToHomeScreen() + onBack() } } diff --git a/feature/onboarding/src/main/java/com/plottwist/feature/onboarding/OnboardingNameViewModel.kt b/feature/onboarding/src/main/java/com/plottwist/feature/onboarding/OnboardingNameViewModel.kt index 25df8bc5..cfa65b99 100644 --- a/feature/onboarding/src/main/java/com/plottwist/feature/onboarding/OnboardingNameViewModel.kt +++ b/feature/onboarding/src/main/java/com/plottwist/feature/onboarding/OnboardingNameViewModel.kt @@ -30,7 +30,7 @@ class OnboardingNameViewModel @Inject constructor( val result = updateOnboardingInfoUseCase(state.name.text.toString()) if (result.isSuccess) { - postSideEffect(OnboardingNameSideEffect.NavigateToHomeScreen) + postSideEffect(OnboardingNameSideEffect.NavigateToBack) } } }