Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

하단 탭 바 구현 #88

Merged
merged 15 commits into from
Jan 31, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.goalpanzi.mission_mate.core.datastore.datasource.AuthDataSource
import com.goalpanzi.mission_mate.core.datastore.datasource.DefaultDataSource
import com.goalpanzi.mission_mate.core.datastore.datasource.MissionDataSource
import com.goalpanzi.mission_mate.core.navigation.NavigationEventHandler
import com.goalpanzi.mission_mate.core.navigation.RouteModel
import com.goalpanzi.mission_mate.core.navigation.model.RouteModel
import com.goalpanzi.mission_mate.core.navigation.di.AuthNavigation
import com.goalpanzi.mission_mate.core.network.TokenExpirationHandler
import kotlinx.coroutines.flow.collect
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.goalpanzi.mission_mate.core.designsystem.ext

import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit

@Composable
fun dpToSp(dp: Dp) : TextUnit = with(LocalDensity.current) { dp.toSp() }
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,12 @@ object MissionMateTypography {
val body_sm_regular = body_sm.copy(
fontWeight = FontWeight.Normal
)
}

private val tab = DefaultTextStyle.copy(
fontSize = 11.sp
bywindow marked this conversation as resolved.
Show resolved Hide resolved
)

val tab_regular = tab.copy(
fontWeight = FontWeight.Normal
)
}
2 changes: 2 additions & 0 deletions core/navigation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ plugins {
alias(libs.plugins.kotlin.plugin.serialization)
alias(libs.plugins.kotlin.ksp)
alias(libs.plugins.hilt.android)
alias(libs.plugins.kotlin.parcelize)
}

android {
Expand Down Expand Up @@ -41,5 +42,6 @@ dependencies {
implementation(libs.kotlin.serialization.json)
implementation(libs.bundles.coroutines)
implementation(libs.hilt.android)
implementation(libs.androidx.navigation.compose)
ksp(libs.hilt.compiler)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.goalpanzi.mission_mate.core.navigation

import com.goalpanzi.mission_mate.core.navigation.model.RouteModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.goalpanzi.mission_mate.core.navigation

import com.goalpanzi.mission_mate.core.navigation.model.RouteModel
import kotlinx.coroutines.flow.SharedFlow

interface NavigationEventHandler {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.goalpanzi.mission_mate.core.navigation.model

import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable

@Serializable
@Parcelize
sealed class MainTabDataModel : Parcelable {
@Serializable
data class Mission(
val isAfterProfileCreate: Boolean = false
) : MainTabDataModel()
@Serializable
data object History : MainTabDataModel()
@Serializable
data object Setting : MainTabDataModel()
@Serializable
data object None : MainTabDataModel()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.goalpanzi.mission_mate.core.navigation.model

import android.os.Build
import android.os.Bundle
import androidx.navigation.NavType
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

val MainTabDataModelType = object : NavType<MainTabDataModel>(
isNullableAllowed = false
){
override fun get(bundle: Bundle, key: String): MainTabDataModel? {
return if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){
bundle.getParcelable(key, MainTabDataModel::class.java)
} else {
@Suppress("DEPRECATION")
bundle.getParcelable(key)
}
}

override fun parseValue(value: String): MainTabDataModel {
return Json.decodeFromString<MainTabDataModel>(value)
}

override fun serializeAsValue(value: MainTabDataModel): String {
return Json.encodeToString(value)
}

override fun put(bundle: Bundle, key: String, value: MainTabDataModel) {
bundle.putParcelable(key, value)
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package com.goalpanzi.mission_mate.core.navigation
package com.goalpanzi.mission_mate.core.navigation.model

import kotlinx.serialization.Serializable


sealed interface RouteModel {
@Serializable
data object Login : RouteModel

@Serializable
data class MainTab(
val mainTabDataModel : MainTabDataModel = MainTabDataModel.None
) : RouteModel

@Serializable
sealed interface Profile: RouteModel {
@Serializable
Expand Down Expand Up @@ -56,7 +60,10 @@ sealed interface RouteModel {
}

@Serializable
sealed interface HistoryRouteModel : MainTabRoute
sealed interface HistoryRouteModel : MainTabRoute {
@Serializable
data object History : HistoryRouteModel
}

@Serializable
sealed interface SettingRouteModel : MainTabRoute {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.goalpanzi.mission_mate.core.ui.util

import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.AnimatedContentTransitionScope.SlideDirection
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.core.tween
import androidx.navigation.NavBackStackEntry

fun AnimatedContentTransitionScope<NavBackStackEntry>.slideInFromEnd(
durationMills: Int = 300
): EnterTransition {
return slideIntoContainer(
towards = SlideDirection.Left,
animationSpec = tween(durationMills)
)
}

fun AnimatedContentTransitionScope<NavBackStackEntry>.slideOutToEnd(
durationMills: Int = 300
): ExitTransition {
return slideOutOfContainer(
towards = SlideDirection.End,
animationSpec = tween(durationMills)
)
}

fun AnimatedContentTransitionScope<NavBackStackEntry>.slideInFromBottom(
durationMills: Int = 300
): EnterTransition {
return slideIntoContainer(
towards = SlideDirection.Up,
animationSpec = tween(durationMills)
)
}

fun AnimatedContentTransitionScope<NavBackStackEntry>.slideOutToBottom(
durationMills: Int = 300
): ExitTransition {
return slideOutOfContainer(
towards = SlideDirection.Down,
animationSpec = tween(durationMills)
)
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.compose.composable
import androidx.navigation.toRoute
import com.goalpanzi.mission_mate.core.navigation.RouteModel.MainTabRoute.MissionRouteModel
import com.goalpanzi.mission_mate.core.navigation.model.RouteModel.MainTabRoute.MissionRouteModel
import com.goalpanzi.mission_mate.core.ui.util.slideInFromEnd
import com.goalpanzi.mission_mate.core.ui.util.slideInFromBottom
import com.goalpanzi.mission_mate.core.ui.util.slideOutToBottom
import com.goalpanzi.mission_mate.core.ui.util.slideOutToEnd
import com.goalpanzi.mission_mate.feature.board.model.CharacterUiModel
import com.goalpanzi.mission_mate.feature.board.model.UserStory
import com.goalpanzi.mission_mate.feature.board.screen.BoardFinishRoute
Expand All @@ -33,15 +37,13 @@ fun NavGraphBuilder.boardNavGraph(
onNavigateDetail : (Long) -> Unit,
onNavigateFinish : (Long) -> Unit,
onNavigateStory: (UserStory) -> Unit,
onClickSetting: () -> Unit,
onNavigateToPreview: (Long, Uri) -> Unit
) {
composable<MissionRouteModel.Board> { navBackStackEntry ->
BoardRoute(
onNavigateOnboarding = onNavigateOnboarding,
onNavigateDetail = onNavigateDetail,
onNavigateFinish = onNavigateFinish,
onClickSetting = onClickSetting,
onClickStory = onNavigateStory,
onPreviewImage = onNavigateToPreview,
)
Expand All @@ -58,7 +60,14 @@ fun NavGraphBuilder.boardDetailNavGraph(
onNavigateOnboarding: () -> Unit,
onBackClick: () -> Unit
) {
composable<MissionRouteModel.Detail> {
composable<MissionRouteModel.Detail>(
enterTransition = {
slideInFromEnd()
},
popExitTransition = {
slideOutToEnd()
}
) {
BoardMissionDetailRoute(
onNavigateOnboarding = onNavigateOnboarding,
onBackClick = onBackClick
Expand All @@ -73,12 +82,10 @@ fun NavController.navigateToBoardFinish(
}

fun NavGraphBuilder.boardFinishNavGraph(
onClickSetting: () -> Unit,
onClickOk: () -> Unit,
) {
composable<MissionRouteModel.Finish> {
BoardFinishRoute(
onSettingClick = onClickSetting,
onOkClick = onClickOk
)
}
Expand All @@ -102,7 +109,14 @@ fun NavController.navigateToUserStory(
fun NavGraphBuilder.userStoryNavGraph(
onClickClose: () -> Unit
) {
composable<MissionRouteModel.UserStory> { backStackEntry ->
composable<MissionRouteModel.UserStory>(
enterTransition = {
slideInFromBottom()
},
exitTransition = {
slideOutToBottom()
}
) { backStackEntry ->
backStackEntry.toRoute<MissionRouteModel.UserStory>().run {
val characterUiModel = userCharacter.let { CharacterUiModel.valueOf(it) }
UserStoryScreen(
Expand All @@ -128,7 +142,14 @@ fun NavGraphBuilder.verificationPreviewNavGraph(
onClickClose: () -> Unit,
onUploadSuccess: () -> Unit
) {
composable<MissionRouteModel.VerificationPreview> {
composable<MissionRouteModel.VerificationPreview>(
enterTransition = {
slideInFromBottom()
},
exitTransition = {
slideOutToBottom()
}
) {
VerificationPreviewRoute(
onClickClose = onClickClose,
onUploadSuccess = { onUploadSuccess() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ fun BoardBottomView(
.navigationBarsPadding()
.clip(RoundedCornerShape(topEnd = 20.dp, topStart = 20.dp))
.background(ColorWhite_FFFFFFFF.copy(alpha = 0.7f))
.padding(top = 16.dp, bottom = 36.dp, start = 24.dp, end = 24.dp),
.padding(top = 16.dp, bottom = 16.dp, start = 24.dp, end = 24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import com.goalpanzi.mission_mate.feature.board.R
import com.goalpanzi.mission_mate.feature.board.model.MissionState
import com.goalpanzi.mission_mate.feature.board.model.UserStory
import com.goalpanzi.mission_mate.core.designsystem.component.StableImage

import com.goalpanzi.mission_mate.core.designsystem.ext.clickableWithoutRipple

@SuppressLint("UnrememberedMutableInteractionSource")
@Composable
Expand All @@ -40,7 +40,6 @@ fun BoardTopView(
missionState : MissionState,
onClickFlag: () -> Unit,
onClickAddUser: () -> Unit,
onClickSetting: () -> Unit,
onClickTooltip : () -> Unit,
onClickStory: (UserStory) -> Unit,
modifier: Modifier = Modifier
Expand Down Expand Up @@ -70,7 +69,6 @@ fun BoardTopView(
BoardTopViewRightActionButtons(
isAddingUserEnabled = isAddingUserEnabled,
onClickAddUser = onClickAddUser,
onClickSetting = onClickSetting
)
},
containerColor = Color.Transparent
Expand All @@ -83,16 +81,11 @@ fun BoardTopView(
)
if(!viewedTooltip){
if (isAddingUserEnabled) {
// datastore 조건 추가
StableImage(
modifier = Modifier
.align(Alignment.TopEnd)
.clickable(
interactionSource = MutableInteractionSource(),
indication = null,
onClick = onClickTooltip
)
.padding(end = 43.dp,top = 56.dp)
.clickableWithoutRipple { onClickTooltip() }
.padding(top = 48.dp)
.width(161.dp),
drawableResId = R.drawable.img_tooltip_mission_invitation_code,
contentScale = ContentScale.Crop
Expand All @@ -101,28 +94,23 @@ fun BoardTopView(
StableImage(
modifier = Modifier
.align(Alignment.TopStart)
.clickable(
interactionSource = MutableInteractionSource(),
indication = null,
onClick = onClickTooltip
)
.padding(start = 8.dp, top = 56.dp)
.clickableWithoutRipple {
onClickTooltip()
}
.padding(start = 8.dp, top = 48.dp)
.width(161.dp),
drawableResId = R.drawable.img_tooltip_mission_detail,
contentScale = ContentScale.Crop
)
}
}


}
}

@Composable
fun BoardTopViewRightActionButtons(
isAddingUserEnabled: Boolean,
onClickAddUser: () -> Unit,
onClickSetting: () -> Unit,
modifier: Modifier = Modifier
) {
Row(
Expand All @@ -141,15 +129,5 @@ fun BoardTopViewRightActionButtons(
)
}
}
IconButton(
onClick = onClickSetting,
modifier = Modifier.wrapContentSize()
) {
Icon(
imageVector = ImageVector.vectorResource(id = com.goalpanzi.mission_mate.core.designsystem.R.drawable.ic_setting),
contentDescription = "",
tint = ColorGray1_FF404249
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import com.goalpanzi.mission_mate.core.domain.common.DomainResult
import com.goalpanzi.mission_mate.core.domain.mission.usecase.DeleteMissionUseCase
import com.goalpanzi.mission_mate.core.domain.mission.usecase.GetMissionUseCase
import com.goalpanzi.mission_mate.core.domain.user.usecase.GetCachedMemberIdUseCase
import com.goalpanzi.mission_mate.core.navigation.RouteModel.MainTabRoute.MissionRouteModel
import com.goalpanzi.mission_mate.core.navigation.model.RouteModel.MainTabRoute.MissionRouteModel
import com.goalpanzi.mission_mate.feature.board.model.MissionError
import com.goalpanzi.mission_mate.feature.board.model.uimodel.MissionUiModel
import dagger.hilt.android.lifecycle.HiltViewModel
Expand Down
Loading