From 48550d87eb4834b0184bc65e78ad7b7f3aaeecc0 Mon Sep 17 00:00:00 2001 From: algosketch Date: Fri, 18 Oct 2024 04:37:03 +0900 Subject: [PATCH 01/90] =?UTF-8?q?refactor=20:=20home=20navigation=20safe?= =?UTF-8?q?=20args=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- presentation/build.gradle.kts | 2 + .../presentation/screen/home/HomeScreen.kt | 59 +++++++++---------- .../screen/navigation/HomeRoute.kt | 29 +++++++++ 3 files changed, 58 insertions(+), 32 deletions(-) create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/HomeRoute.kt diff --git a/presentation/build.gradle.kts b/presentation/build.gradle.kts index bf49cbe22..c8c6b0e78 100644 --- a/presentation/build.gradle.kts +++ b/presentation/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.android) alias(libs.plugins.hilt) + alias(libs.plugins.kotlin.serialization) id("kotlin-kapt") id("kotlin-parcelize") } @@ -77,6 +78,7 @@ dependencies { implementation(libs.hilt.android) implementation(libs.androidx.hilt.navigation.compose) + implementation(libs.kotlinx.serialization.json) implementation(libs.androidx.material3.android) implementation(libs.zoomable) kapt(libs.hilt.compiler) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt index ef8df8688..417fb081a 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt @@ -2,8 +2,6 @@ package com.nexters.boolti.presentation.screen.home import android.content.Intent import android.net.Uri -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -17,9 +15,9 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier @@ -32,12 +30,13 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.navDeepLink import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.extension.requireActivity import com.nexters.boolti.presentation.screen.LocalSnackbarController import com.nexters.boolti.presentation.screen.my.MyScreen +import com.nexters.boolti.presentation.screen.navigation.HomeRoute +import com.nexters.boolti.presentation.screen.navigation.homeRoutes import com.nexters.boolti.presentation.screen.show.ShowScreen import com.nexters.boolti.presentation.screen.ticket.TicketLoginScreen import com.nexters.boolti.presentation.screen.ticket.TicketScreen @@ -61,8 +60,7 @@ fun HomeScreen( ) { val navController = rememberNavControllerWithLog() val snackbarController = LocalSnackbarController.current - val navBackStackEntry by navController.currentBackStackEntryAsState() - val currentDestination = navBackStackEntry?.destination?.route ?: Destination.Show.route + var currentRoute: HomeRoute by remember { mutableStateOf(HomeRoute.Show) } val loggedIn by viewModel.loggedIn.collectAsStateWithLifecycle() val context = LocalContext.current @@ -105,9 +103,9 @@ fun HomeScreen( Scaffold( bottomBar = { HomeNavigationBar( - currentDestination = currentDestination, - onDestinationChanged = { - navController.navigate(it.route) { + currentDestination = currentRoute, + onDestinationChanged = { dest -> + navController.navigate(dest) { popUpTo(navController.graph.findStartDestination().id) launchSingleTop = true restoreState = true @@ -118,11 +116,10 @@ fun HomeScreen( ) { innerPadding -> NavHost( navController = navController, - startDestination = Destination.Show.route, + startDestination = HomeRoute.Show, modifier = modifier, ) { - composable( - route = Destination.Show.route, + composable( deepLinks = listOf( navDeepLink { uriPattern = "https://app.boolti.in/home/shows" @@ -130,14 +127,17 @@ fun HomeScreen( } ) ) { + LaunchedEffect(Unit) { + currentRoute = HomeRoute.Show + } + ShowScreen( modifier = modifier.padding(innerPadding), onClickShowItem = onClickShowItem, navigateToBusiness = navigateToBusiness, ) } - composable( - route = Destination.Ticket.route, + composable( deepLinks = listOf( navDeepLink { uriPattern = "https://app.boolti.in/home/tickets" @@ -145,6 +145,10 @@ fun HomeScreen( } ) ) { + LaunchedEffect(Unit) { + currentRoute = HomeRoute.Ticket + } + when (loggedIn) { true -> TicketScreen( onClickTicket = onClickTicket, @@ -159,9 +163,11 @@ fun HomeScreen( else -> Unit // 로그인 여부를 불러오는 중 } } - composable( - route = Destination.My.route, - ) { + composable { + LaunchedEffect(Unit) { + currentRoute = HomeRoute.My + } + MyScreen( modifier = modifier.padding(innerPadding), requireLogin = requireLogin, @@ -194,21 +200,10 @@ fun HomeScreen( } } -@Stable -private enum class Destination( - val route: String, - @StringRes val label: Int, - @DrawableRes val icon: Int, -) { - Show(route = "show", label = R.string.menu_show, icon = R.drawable.ic_home), - Ticket(route = "tickets", label = R.string.menu_tickets, R.drawable.ic_ticket), - My(route = "my", label = R.string.menu_my, icon = R.drawable.ic_person) -} - @Composable private fun HomeNavigationBar( - currentDestination: String, - onDestinationChanged: (Destination) -> Unit, + currentDestination: HomeRoute, + onDestinationChanged: (HomeRoute) -> Unit, modifier: Modifier = Modifier, ) { Column { @@ -221,8 +216,8 @@ private fun HomeNavigationBar( modifier = modifier, containerColor = MaterialTheme.colorScheme.background, ) { - Destination.entries.forEach { dest -> - val selected = currentDestination == dest.route + homeRoutes.forEach { dest -> + val selected = currentDestination == dest val label = stringResource(dest.label) NavigationBarItem( selected = selected, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/HomeRoute.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/HomeRoute.kt new file mode 100644 index 000000000..6370d375c --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/HomeRoute.kt @@ -0,0 +1,29 @@ +package com.nexters.boolti.presentation.screen.navigation + +import com.nexters.boolti.presentation.R +import kotlinx.serialization.Serializable + +sealed interface HomeRoute { + val label: Int + val icon: Int + + @Serializable + data object Show : HomeRoute { + override val label = R.string.menu_show + override val icon = R.drawable.ic_home + } + + @Serializable + data object Ticket : HomeRoute { + override val label = R.string.menu_tickets + override val icon = R.drawable.ic_ticket + } + + @Serializable + data object My : HomeRoute { + override val label = R.string.menu_my + override val icon: Int = R.drawable.ic_person + } +} + +val homeRoutes = listOf(HomeRoute.Show, HomeRoute.Ticket, HomeRoute.My) \ No newline at end of file From 75e78f557959f965360fadea2e64959220ea1815 Mon Sep 17 00:00:00 2001 From: algosketch Date: Fri, 18 Oct 2024 04:41:29 +0900 Subject: [PATCH 02/90] =?UTF-8?q?refactor=20:=20innerPadding=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nexters/boolti/presentation/screen/home/HomeScreen.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt index 417fb081a..6f28c4408 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt @@ -115,9 +115,9 @@ fun HomeScreen( } ) { innerPadding -> NavHost( + modifier = modifier.padding(innerPadding), navController = navController, startDestination = HomeRoute.Show, - modifier = modifier, ) { composable( deepLinks = listOf( @@ -132,7 +132,6 @@ fun HomeScreen( } ShowScreen( - modifier = modifier.padding(innerPadding), onClickShowItem = onClickShowItem, navigateToBusiness = navigateToBusiness, ) @@ -152,7 +151,6 @@ fun HomeScreen( when (loggedIn) { true -> TicketScreen( onClickTicket = onClickTicket, - modifier = modifier.padding(innerPadding), ) false -> TicketLoginScreen( @@ -169,7 +167,6 @@ fun HomeScreen( } MyScreen( - modifier = modifier.padding(innerPadding), requireLogin = requireLogin, onClickAccountSetting = onClickAccountSetting, navigateToReservations = navigateToReservations, From 289f2bb353d43db5ebe28e594b03910dd10cbf5b Mon Sep 17 00:00:00 2001 From: algosketch Date: Fri, 18 Oct 2024 04:56:57 +0900 Subject: [PATCH 03/90] =?UTF-8?q?refactor=20:=20composable=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EA=B0=81=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EB=A1=9C=20=EC=B6=94=EC=B6=9C=ED=95=98?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/screen/home/HomeScreen.kt | 80 +++++-------------- .../presentation/screen/my/MyNavigation.kt | 29 +++++++ .../boolti/presentation/screen/my/MyScreen.kt | 4 +- .../screen/show/ShowNavigation.kt | 33 ++++++++ .../screen/ticket/TicketNavigation.kt | 41 ++++++++++ 5 files changed, 127 insertions(+), 60 deletions(-) create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt index 6f28c4408..456ec2959 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt @@ -1,6 +1,5 @@ package com.nexters.boolti.presentation.screen.home -import android.content.Intent import android.net.Uri import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth @@ -30,16 +29,15 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable -import androidx.navigation.navDeepLink import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.extension.requireActivity import com.nexters.boolti.presentation.screen.LocalSnackbarController import com.nexters.boolti.presentation.screen.my.MyScreen +import com.nexters.boolti.presentation.screen.my.addMy import com.nexters.boolti.presentation.screen.navigation.HomeRoute import com.nexters.boolti.presentation.screen.navigation.homeRoutes -import com.nexters.boolti.presentation.screen.show.ShowScreen -import com.nexters.boolti.presentation.screen.ticket.TicketLoginScreen -import com.nexters.boolti.presentation.screen.ticket.TicketScreen +import com.nexters.boolti.presentation.screen.show.addShow +import com.nexters.boolti.presentation.screen.ticket.addTicket import com.nexters.boolti.presentation.theme.Grey10 import com.nexters.boolti.presentation.theme.Grey50 import com.nexters.boolti.presentation.theme.Grey85 @@ -119,61 +117,27 @@ fun HomeScreen( navController = navController, startDestination = HomeRoute.Show, ) { - composable( - deepLinks = listOf( - navDeepLink { - uriPattern = "https://app.boolti.in/home/shows" - action = Intent.ACTION_VIEW - } - ) - ) { - LaunchedEffect(Unit) { - currentRoute = HomeRoute.Show - } - - ShowScreen( - onClickShowItem = onClickShowItem, - navigateToBusiness = navigateToBusiness, - ) - } - composable( - deepLinks = listOf( - navDeepLink { - uriPattern = "https://app.boolti.in/home/tickets" - action = Intent.ACTION_VIEW - } - ) - ) { - LaunchedEffect(Unit) { - currentRoute = HomeRoute.Ticket - } - - when (loggedIn) { - true -> TicketScreen( - onClickTicket = onClickTicket, - ) - - false -> TicketLoginScreen( - modifier.padding(innerPadding), - onLoginClick = requireLogin - ) + addShow( + updateRoute = { currentRoute = HomeRoute.Show }, + onClickShowItem = onClickShowItem, + navigateToBusiness = navigateToBusiness, + ) - else -> Unit // 로그인 여부를 불러오는 중 - } - } - composable { - LaunchedEffect(Unit) { - currentRoute = HomeRoute.My - } + addTicket( + updateRoute = { currentRoute = HomeRoute.Ticket }, + loggedIn = loggedIn, + onLoginClick = requireLogin, + onClickTicket = onClickTicket, + ) - MyScreen( - requireLogin = requireLogin, - onClickAccountSetting = onClickAccountSetting, - navigateToReservations = navigateToReservations, - navigateToProfile = navigateToProfile, - onClickQrScan = onClickQrScan, - ) - } + addMy( + updateRoute = { currentRoute = HomeRoute.My }, + requireLogin = requireLogin, + onClickAccountSetting = onClickAccountSetting, + navigateToReservations = navigateToReservations, + navigateToProfile = navigateToProfile, + onClickQrScan = onClickQrScan, + ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt new file mode 100644 index 000000000..0c9da6930 --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt @@ -0,0 +1,29 @@ +package com.nexters.boolti.presentation.screen.my + +import androidx.compose.runtime.LaunchedEffect +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.navigation.HomeRoute + +fun NavGraphBuilder.addMy( + updateRoute: () -> Unit, + requireLogin: () -> Unit, + onClickAccountSetting: () -> Unit, + navigateToReservations: () -> Unit, + navigateToProfile: () -> Unit, + onClickQrScan: () -> Unit, +) { + composable { + LaunchedEffect(Unit) { + updateRoute() + } + + MyScreen( + requireLogin = requireLogin, + onClickAccountSetting = onClickAccountSetting, + navigateToReservations = navigateToReservations, + navigateToProfile = navigateToProfile, + onClickQrScan = onClickQrScan, + ) + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt index 37bb877d2..b8cfc5ac9 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt @@ -50,13 +50,13 @@ import com.nexters.boolti.presentation.theme.point3 @Composable fun MyScreen( - modifier: Modifier = Modifier, - viewModel: MyViewModel = hiltViewModel(), requireLogin: () -> Unit, onClickAccountSetting: () -> Unit, navigateToReservations: () -> Unit, navigateToProfile: () -> Unit, onClickQrScan: () -> Unit, + modifier: Modifier = Modifier, + viewModel: MyViewModel = hiltViewModel(), ) { val user by viewModel.user.collectAsStateWithLifecycle() val uriHandler = LocalUriHandler.current diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt new file mode 100644 index 000000000..d2cee2644 --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt @@ -0,0 +1,33 @@ +package com.nexters.boolti.presentation.screen.show + +import android.content.Intent +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.LaunchedEffect +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import androidx.navigation.navDeepLink +import com.nexters.boolti.presentation.screen.navigation.HomeRoute + +fun NavGraphBuilder.addShow( + updateRoute: () -> Unit, + onClickShowItem: (showId: String) -> Unit, + navigateToBusiness: () -> Unit, +) { + composable( + deepLinks = listOf( + navDeepLink { + uriPattern = "https://app.boolti.in/home/shows" + action = Intent.ACTION_VIEW + } + ) + ) { + LaunchedEffect(Unit) { + updateRoute() + } + + ShowScreen( + onClickShowItem = onClickShowItem, + navigateToBusiness = navigateToBusiness, + ) + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt new file mode 100644 index 000000000..87686de38 --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt @@ -0,0 +1,41 @@ +package com.nexters.boolti.presentation.screen.ticket + +import android.content.Intent +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.LaunchedEffect +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import androidx.navigation.navDeepLink +import com.nexters.boolti.presentation.screen.navigation.HomeRoute + +fun NavGraphBuilder.addTicket( + updateRoute: () -> Unit, + loggedIn: Boolean?, + onClickTicket: (String) -> Unit, + onLoginClick: () -> Unit, +) { + composable( + deepLinks = listOf( + navDeepLink { + uriPattern = "https://app.boolti.in/home/tickets" + action = Intent.ACTION_VIEW + } + ) + ) { + LaunchedEffect(Unit) { + updateRoute() + } + + when (loggedIn) { + true -> TicketScreen( + onClickTicket = onClickTicket, + ) + + false -> TicketLoginScreen( + onLoginClick = onLoginClick + ) + + else -> Unit // 로그인 여부를 불러오는 중 + } + } +} \ No newline at end of file From 4c2c3a890282d57325fdec05883f1b67f60a8b0d Mon Sep 17 00:00:00 2001 From: algosketch Date: Fri, 18 Oct 2024 05:09:43 +0900 Subject: [PATCH 04/90] =?UTF-8?q?refactor=20:=20HomeScreen=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8A=94=20=EB=B3=80?= =?UTF-8?q?=EC=88=98,=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 예를 들어 onClickShow 라는 람다를 넘겨주려면 이 화면에서 공연이 클릭 되었을 때 비즈니스 로직을 알아야 한다. 대신 navigateToXXX 라는 이름으로 변경해 어떤 람다를 넣어줘야 하는지 쉽게 알도록 했다. --- .../screen/home/HomeNavigation.kt | 10 +++--- .../presentation/screen/home/HomeScreen.kt | 36 +++++++++---------- .../presentation/screen/my/MyNavigation.kt | 12 +++---- .../screen/show/ShowNavigation.kt | 5 ++- .../screen/ticket/TicketNavigation.kt | 13 ++++--- 5 files changed, 36 insertions(+), 40 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index 1fe8e296a..5e349e72e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -14,14 +14,14 @@ fun NavGraphBuilder.HomeScreen( ) { HomeScreen( modifier = modifier, - onClickShowItem = { navigateTo("${MainDestination.ShowDetail.route}/$it") }, - onClickTicket = { navigateTo("${MainDestination.TicketDetail.route}/$it") }, - onClickQrScan = { navigateTo(MainDestination.HostedShows.route) }, - onClickAccountSetting = { navigateTo(MainDestination.AccountSetting.route) }, + navigateToShowDetail = { navigateTo("${MainDestination.ShowDetail.route}/$it") }, + navigateToTicketDetail = { navigateTo("${MainDestination.TicketDetail.route}/$it") }, + navigateToQrScan = { navigateTo(MainDestination.HostedShows.route) }, + navigateToAccountSetting = { navigateTo(MainDestination.AccountSetting.route) }, navigateToReservations = { navigateTo(MainDestination.Reservations.route) }, navigateToProfile = { navigateTo(MainDestination.Profile.createRoute()) }, navigateToBusiness = { navigateTo(MainDestination.Business.route) }, - requireLogin = { navigateTo(MainDestination.Login.route) }, + navigateToLogin = { navigateTo(MainDestination.Login.route) }, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt index 456ec2959..ded42bfd5 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt @@ -28,11 +28,9 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.extension.requireActivity import com.nexters.boolti.presentation.screen.LocalSnackbarController -import com.nexters.boolti.presentation.screen.my.MyScreen import com.nexters.boolti.presentation.screen.my.addMy import com.nexters.boolti.presentation.screen.navigation.HomeRoute import com.nexters.boolti.presentation.screen.navigation.homeRoutes @@ -45,22 +43,22 @@ import com.nexters.boolti.presentation.util.rememberNavControllerWithLog @Composable fun HomeScreen( - onClickShowItem: (showId: String) -> Unit, - onClickTicket: (ticketId: String) -> Unit, - onClickQrScan: () -> Unit, - onClickAccountSetting: () -> Unit, + navigateToShowDetail: (showId: String) -> Unit, + navigateToTicketDetail: (ticketId: String) -> Unit, + navigateToQrScan: () -> Unit, + navigateToAccountSetting: () -> Unit, navigateToReservations: () -> Unit, navigateToProfile: () -> Unit, navigateToBusiness: () -> Unit, - requireLogin: () -> Unit, - modifier: Modifier, + navigateToLogin: () -> Unit, + modifier: Modifier = Modifier, viewModel: HomeViewModel = hiltViewModel(), ) { val navController = rememberNavControllerWithLog() val snackbarController = LocalSnackbarController.current var currentRoute: HomeRoute by remember { mutableStateOf(HomeRoute.Show) } - val loggedIn by viewModel.loggedIn.collectAsStateWithLifecycle() + val isLoggedIn by viewModel.loggedIn.collectAsStateWithLifecycle() val context = LocalContext.current val giftRegistrationMessage = stringResource(id = R.string.gift_successfully_registered) @@ -94,8 +92,8 @@ fun HomeScreen( } } - LaunchedEffect(loggedIn) { - if (loggedIn == true) viewModel.processGift() + LaunchedEffect(isLoggedIn) { + if (isLoggedIn == true) viewModel.processGift() } Scaffold( @@ -119,24 +117,24 @@ fun HomeScreen( ) { addShow( updateRoute = { currentRoute = HomeRoute.Show }, - onClickShowItem = onClickShowItem, + navigateToShowDetail = navigateToShowDetail, navigateToBusiness = navigateToBusiness, ) addTicket( updateRoute = { currentRoute = HomeRoute.Ticket }, - loggedIn = loggedIn, - onLoginClick = requireLogin, - onClickTicket = onClickTicket, + isLoggedIn = isLoggedIn, + navigateToLogin = navigateToLogin, + navigateToTicketDetail = navigateToTicketDetail, ) addMy( updateRoute = { currentRoute = HomeRoute.My }, - requireLogin = requireLogin, - onClickAccountSetting = onClickAccountSetting, + navigateToLogin = navigateToLogin, + navigateToAccountSetting = navigateToAccountSetting, navigateToReservations = navigateToReservations, navigateToProfile = navigateToProfile, - onClickQrScan = onClickQrScan, + navigateToQrScan = navigateToQrScan, ) } } @@ -151,7 +149,7 @@ fun HomeScreen( receiveGift = viewModel::receiveGift, requireLogin = { dialog = null - requireLogin() + navigateToLogin() }, onFailed = { dialog = GiftStatus.FAILED diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt index 0c9da6930..05c5d77f9 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt @@ -7,11 +7,11 @@ import com.nexters.boolti.presentation.screen.navigation.HomeRoute fun NavGraphBuilder.addMy( updateRoute: () -> Unit, - requireLogin: () -> Unit, - onClickAccountSetting: () -> Unit, + navigateToLogin: () -> Unit, + navigateToAccountSetting: () -> Unit, navigateToReservations: () -> Unit, navigateToProfile: () -> Unit, - onClickQrScan: () -> Unit, + navigateToQrScan: () -> Unit, ) { composable { LaunchedEffect(Unit) { @@ -19,11 +19,11 @@ fun NavGraphBuilder.addMy( } MyScreen( - requireLogin = requireLogin, - onClickAccountSetting = onClickAccountSetting, + requireLogin = navigateToLogin, + onClickAccountSetting = navigateToAccountSetting, navigateToReservations = navigateToReservations, navigateToProfile = navigateToProfile, - onClickQrScan = onClickQrScan, + onClickQrScan = navigateToQrScan, ) } } \ No newline at end of file diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt index d2cee2644..6e07561e2 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt @@ -1,7 +1,6 @@ package com.nexters.boolti.presentation.screen.show import android.content.Intent -import androidx.compose.foundation.layout.padding import androidx.compose.runtime.LaunchedEffect import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable @@ -10,7 +9,7 @@ import com.nexters.boolti.presentation.screen.navigation.HomeRoute fun NavGraphBuilder.addShow( updateRoute: () -> Unit, - onClickShowItem: (showId: String) -> Unit, + navigateToShowDetail: (showId: String) -> Unit, navigateToBusiness: () -> Unit, ) { composable( @@ -26,7 +25,7 @@ fun NavGraphBuilder.addShow( } ShowScreen( - onClickShowItem = onClickShowItem, + onClickShowItem = navigateToShowDetail, navigateToBusiness = navigateToBusiness, ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt index 87686de38..f5dba852b 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt @@ -1,7 +1,6 @@ package com.nexters.boolti.presentation.screen.ticket import android.content.Intent -import androidx.compose.foundation.layout.padding import androidx.compose.runtime.LaunchedEffect import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable @@ -10,9 +9,9 @@ import com.nexters.boolti.presentation.screen.navigation.HomeRoute fun NavGraphBuilder.addTicket( updateRoute: () -> Unit, - loggedIn: Boolean?, - onClickTicket: (String) -> Unit, - onLoginClick: () -> Unit, + isLoggedIn: Boolean?, + navigateToTicketDetail: (String) -> Unit, + navigateToLogin: () -> Unit, ) { composable( deepLinks = listOf( @@ -26,13 +25,13 @@ fun NavGraphBuilder.addTicket( updateRoute() } - when (loggedIn) { + when (isLoggedIn) { true -> TicketScreen( - onClickTicket = onClickTicket, + onClickTicket = navigateToTicketDetail, ) false -> TicketLoginScreen( - onLoginClick = onLoginClick + onLoginClick = navigateToLogin ) else -> Unit // 로그인 여부를 불러오는 중 From b3174f6be7f4fe02b3b537dfe2c476d1df317c2d Mon Sep 17 00:00:00 2001 From: algosketch Date: Mon, 21 Oct 2024 00:05:27 +0900 Subject: [PATCH 05/90] =?UTF-8?q?add=20:=20main=20route=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../screen/navigation/MainRoute.kt | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt new file mode 100644 index 000000000..710e1e35e --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt @@ -0,0 +1,92 @@ +package com.nexters.boolti.presentation.screen.navigation + +import kotlinx.serialization.Serializable + +sealed interface MainRoute { + @Serializable + data object Home : MainRoute + + @Serializable + data class ShowDetail( + val showId: String, + ) : MainRoute + + @Serializable + data class Ticketing( + val showId: String, + val salesTicketId: String, + val ticketCount: Int, + val isInviteTicket: Boolean, + ) : MainRoute + + @Serializable + data class Gift( + val showId: String, + val salesTicketId: String, + val ticketCount: Int, + ) : MainRoute + + @Serializable + data class PaymentComplete( + val reservationId: String, + val showId: String, + ) : MainRoute + + @Serializable + data class GiftComplete( + val giftId: String, + ) : MainRoute + + @Serializable + data class TicketDetail( + val ticketId: String, + ) + + @Serializable + data object Qr : MainRoute + + @Serializable + data object Reservations : MainRoute + + @Serializable + data class ReservationDetail( + val reservationId: String, + val isGift: Boolean, + ) : MainRoute + + @Serializable + data class Refund( + val reservationId: String, + val isGift: Boolean + ) : MainRoute + + @Serializable + data object HostedShows : MainRoute + + @Serializable + data object SignOut : MainRoute + + @Serializable + data object Login : MainRoute + + @Serializable + data object Business : MainRoute + + @Serializable + data object AccountSetting : MainRoute + + @Serializable + data class Profile( + val userCode: String? + ) : MainRoute + + @Serializable + data object ProfileEdit : MainRoute + + @Serializable + data class ProfileLinkEdit( + val linkId: String?, + val linkTitle: String?, + val url: String? + ) : MainRoute +} From c6b101d1d54dfe568a484d186c2f4d61ccf1695b Mon Sep 17 00:00:00 2001 From: algosketch Date: Tue, 22 Oct 2024 23:16:23 +0900 Subject: [PATCH 06/90] =?UTF-8?q?refactor=20:=20current=20destination=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=EB=A5=BC=20=ED=86=B5=ED=95=B4=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/screen/home/HomeScreen.kt | 17 ++++++++--------- .../presentation/screen/my/MyNavigation.kt | 6 ------ .../presentation/screen/show/ShowNavigation.kt | 6 ------ .../screen/ticket/TicketNavigation.kt | 6 ------ 4 files changed, 8 insertions(+), 27 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt index ded42bfd5..d7b5c761e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt @@ -16,7 +16,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier @@ -26,8 +25,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavDestination +import androidx.navigation.NavDestination.Companion.hasRoute import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.compose.NavHost +import androidx.navigation.compose.currentBackStackEntryAsState import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.extension.requireActivity import com.nexters.boolti.presentation.screen.LocalSnackbarController @@ -54,9 +56,9 @@ fun HomeScreen( modifier: Modifier = Modifier, viewModel: HomeViewModel = hiltViewModel(), ) { - val navController = rememberNavControllerWithLog() val snackbarController = LocalSnackbarController.current - var currentRoute: HomeRoute by remember { mutableStateOf(HomeRoute.Show) } + val navController = rememberNavControllerWithLog() + val currentBackStack by navController.currentBackStackEntryAsState() val isLoggedIn by viewModel.loggedIn.collectAsStateWithLifecycle() val context = LocalContext.current @@ -99,7 +101,7 @@ fun HomeScreen( Scaffold( bottomBar = { HomeNavigationBar( - currentDestination = currentRoute, + currentDestination = currentBackStack?.destination, onDestinationChanged = { dest -> navController.navigate(dest) { popUpTo(navController.graph.findStartDestination().id) @@ -116,20 +118,17 @@ fun HomeScreen( startDestination = HomeRoute.Show, ) { addShow( - updateRoute = { currentRoute = HomeRoute.Show }, navigateToShowDetail = navigateToShowDetail, navigateToBusiness = navigateToBusiness, ) addTicket( - updateRoute = { currentRoute = HomeRoute.Ticket }, isLoggedIn = isLoggedIn, navigateToLogin = navigateToLogin, navigateToTicketDetail = navigateToTicketDetail, ) addMy( - updateRoute = { currentRoute = HomeRoute.My }, navigateToLogin = navigateToLogin, navigateToAccountSetting = navigateToAccountSetting, navigateToReservations = navigateToReservations, @@ -161,7 +160,7 @@ fun HomeScreen( @Composable private fun HomeNavigationBar( - currentDestination: HomeRoute, + currentDestination: NavDestination?, onDestinationChanged: (HomeRoute) -> Unit, modifier: Modifier = Modifier, ) { @@ -176,7 +175,7 @@ private fun HomeNavigationBar( containerColor = MaterialTheme.colorScheme.background, ) { homeRoutes.forEach { dest -> - val selected = currentDestination == dest + val selected = currentDestination?.hasRoute(dest::class) ?: false val label = stringResource(dest.label) NavigationBarItem( selected = selected, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt index 05c5d77f9..56349cfe4 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt @@ -1,12 +1,10 @@ package com.nexters.boolti.presentation.screen.my -import androidx.compose.runtime.LaunchedEffect import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.navigation.HomeRoute fun NavGraphBuilder.addMy( - updateRoute: () -> Unit, navigateToLogin: () -> Unit, navigateToAccountSetting: () -> Unit, navigateToReservations: () -> Unit, @@ -14,10 +12,6 @@ fun NavGraphBuilder.addMy( navigateToQrScan: () -> Unit, ) { composable { - LaunchedEffect(Unit) { - updateRoute() - } - MyScreen( requireLogin = navigateToLogin, onClickAccountSetting = navigateToAccountSetting, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt index 6e07561e2..e8532fc3b 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt @@ -1,14 +1,12 @@ package com.nexters.boolti.presentation.screen.show import android.content.Intent -import androidx.compose.runtime.LaunchedEffect import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import androidx.navigation.navDeepLink import com.nexters.boolti.presentation.screen.navigation.HomeRoute fun NavGraphBuilder.addShow( - updateRoute: () -> Unit, navigateToShowDetail: (showId: String) -> Unit, navigateToBusiness: () -> Unit, ) { @@ -20,10 +18,6 @@ fun NavGraphBuilder.addShow( } ) ) { - LaunchedEffect(Unit) { - updateRoute() - } - ShowScreen( onClickShowItem = navigateToShowDetail, navigateToBusiness = navigateToBusiness, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt index f5dba852b..e1c4293aa 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt @@ -1,14 +1,12 @@ package com.nexters.boolti.presentation.screen.ticket import android.content.Intent -import androidx.compose.runtime.LaunchedEffect import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import androidx.navigation.navDeepLink import com.nexters.boolti.presentation.screen.navigation.HomeRoute fun NavGraphBuilder.addTicket( - updateRoute: () -> Unit, isLoggedIn: Boolean?, navigateToTicketDetail: (String) -> Unit, navigateToLogin: () -> Unit, @@ -21,10 +19,6 @@ fun NavGraphBuilder.addTicket( } ) ) { - LaunchedEffect(Unit) { - updateRoute() - } - when (isLoggedIn) { true -> TicketScreen( onClickTicket = navigateToTicketDetail, From 299d5f3990e97c1f78acd5e912051c4e35e9a1f4 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 20:40:20 +0900 Subject: [PATCH 07/90] =?UTF-8?q?refactor=20:=20modifier=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/Main.kt | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 01bb352f4..5a6e00f0a 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -17,6 +17,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.ViewModel import androidx.navigation.NavBackStackEntry import androidx.navigation.NavController +import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.navigation import androidx.navigation.navDeepLink @@ -87,15 +88,18 @@ fun Main(onClickQrScan: (showId: String, showName: String) -> Unit) { } @Composable -fun MainNavigation(modifier: Modifier, onClickQrScan: (showId: String, showName: String) -> Unit) { - val navController = rememberNavControllerWithLog() - +fun MainNavigation( + onClickQrScan: (showId: String, showName: String) -> Unit, + modifier: Modifier = Modifier, + navController: NavHostController = rememberNavControllerWithLog(), +) { NavHost( + modifier = modifier, navController = navController, startDestination = Home.route, ) { - HomeScreen(modifier = modifier, navigateTo = navController::navigateTo) - LoginScreen(modifier = modifier, popBackStack = navController::popBackStack) + HomeScreen(navigateTo = navController::navigateTo) + LoginScreen(popBackStack = navController::popBackStack) SignoutScreen( navigateToHome = navController::navigateToHome, popBackStack = navController::popBackStack @@ -122,7 +126,6 @@ fun MainNavigation(modifier: Modifier, onClickQrScan: (showId: String, showName: ), ) { ShowDetailScreen( - modifier = modifier, navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, navigateToHome = navController::navigateToHome, @@ -133,19 +136,16 @@ fun MainNavigation(modifier: Modifier, onClickQrScan: (showId: String, showName: getSharedViewModel = { entry -> entry.sharedViewModel(navController) } ) ShowDetailContentScreen( - modifier = modifier, popBackStack = navController::popBackStack, getSharedViewModel = { entry -> entry.sharedViewModel(navController) } ) ReportScreen( - modifier = modifier, navigateToHome = navController::navigateToHome, popBackStack = navController::popBackStack, ) } TicketingScreen( - modifier = modifier, navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, ) @@ -162,26 +162,22 @@ fun MainNavigation(modifier: Modifier, onClickQrScan: (showId: String, showName: arguments = MainDestination.TicketDetail.arguments, ) { TicketDetailScreen( - modifier = modifier, navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, getSharedViewModel = { entry -> entry.sharedViewModel(navController) }, ) QrFullScreen( - modifier = modifier, popBackStack = navController::popBackStack, getSharedViewModel = { entry -> entry.sharedViewModel(navController) }, ) } addGiftScreen( - modifier = modifier, navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, ) HostedShowScreen( - modifier = modifier, onClickShow = onClickQrScan, popBackStack = navController::popBackStack, ) @@ -209,7 +205,6 @@ fun MainNavigation(modifier: Modifier, onClickQrScan: (showId: String, showName: popBackStack = navController::popBackStack, ) ProfileScreen( - modifier = modifier, navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, ) @@ -218,12 +213,10 @@ fun MainNavigation(modifier: Modifier, onClickQrScan: (showId: String, showName: startDestination = MainDestination.ProfileEdit.route, ) { ProfileEditScreen( - modifier = modifier, navigateTo = navController::navigate, popBackStack = navController::popBackStack, ) ProfileLinkEditScreen( - modifier = modifier, onAddLink = { linkName, url -> navController.previousBackStackEntry ?.savedStateHandle From 3e3c0817eec518f950dfec7cde84a2fe074eb775 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 20:45:39 +0900 Subject: [PATCH 08/90] =?UTF-8?q?refactor=20:=20=EB=82=B4=EB=B9=84?= =?UTF-8?q?=EA=B2=8C=EC=9D=B4=EC=85=98=20=EC=BB=B4=ED=8F=AC=EC=A0=80?= =?UTF-8?q?=EB=B8=94=20=ED=95=A8=EC=88=98=20=EB=AA=85=20=EC=B9=B4=EB=A9=9C?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=8A=A4=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReservationDetailNavigation.kt | 2 +- .../boolti/presentation/screen/Main.kt | 88 +++++++++---------- .../AccountSettingNavigation.kt | 2 +- .../screen/business/BusinessNavigation.kt | 2 +- .../screen/gift/GiftNavigation.kt | 2 +- .../giftcomplete/GiftCompleteNavigation.kt | 2 +- .../screen/home/HomeNavigation.kt | 2 +- .../screen/login/LoginNavigation.kt | 3 +- .../screen/payment/PaymentNavigation.kt | 2 +- .../screen/profile/ProfileNavigation.kt | 2 +- .../link/ProfileLinkEditNavigation.kt | 2 +- .../profile/ProfileEditNavigation.kt | 2 +- .../screen/qr/HostedShowNavigation.kt | 2 +- .../presentation/screen/qr/QrFullScreen.kt | 2 +- .../screen/refund/RefundNavigation.kt | 3 +- .../screen/report/ReportNavigation.kt | 2 +- .../reservations/ReservationsNavigation.kt | 2 +- .../showdetail/ShowDetailContentNavigation.kt | 2 +- .../screen/showdetail/ShowDetailNavigation.kt | 2 +- .../screen/showdetail/ShowImagesNavigation.kt | 2 +- .../screen/signout/SignoutNavigation.kt | 2 +- .../ticket/detail/TicketDetailScreen.kt | 2 +- .../screen/ticketing/TicketingNavigation.kt | 2 +- 23 files changed, 66 insertions(+), 68 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt index ff5ed426d..a06888800 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt @@ -4,7 +4,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination -fun NavGraphBuilder.ReservationDetailScreen( +fun NavGraphBuilder.reservationDetailScreen( navigateTo: (String) -> Unit, popBackStack: () -> Unit, ) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 5a6e00f0a..f0dc0fe49 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -23,30 +23,30 @@ import androidx.navigation.compose.navigation import androidx.navigation.navDeepLink import com.nexters.boolti.presentation.component.ToastSnackbarHost import com.nexters.boolti.presentation.extension.navigateToHome -import com.nexters.boolti.presentation.reservationdetail.ReservationDetailScreen +import com.nexters.boolti.presentation.reservationdetail.reservationDetailScreen import com.nexters.boolti.presentation.screen.MainDestination.Home import com.nexters.boolti.presentation.screen.MainDestination.ShowDetail -import com.nexters.boolti.presentation.screen.accountsetting.AccountSettingScreen -import com.nexters.boolti.presentation.screen.business.BusinessScreen -import com.nexters.boolti.presentation.screen.gift.addGiftScreen -import com.nexters.boolti.presentation.screen.giftcomplete.addGiftCompleteScreen -import com.nexters.boolti.presentation.screen.home.HomeScreen -import com.nexters.boolti.presentation.screen.login.LoginScreen -import com.nexters.boolti.presentation.screen.payment.PaymentCompleteScreen -import com.nexters.boolti.presentation.screen.profile.ProfileScreen -import com.nexters.boolti.presentation.screen.profileedit.link.ProfileLinkEditScreen -import com.nexters.boolti.presentation.screen.profileedit.profile.ProfileEditScreen -import com.nexters.boolti.presentation.screen.qr.HostedShowScreen -import com.nexters.boolti.presentation.screen.qr.QrFullScreen -import com.nexters.boolti.presentation.screen.refund.RefundScreen -import com.nexters.boolti.presentation.screen.report.ReportScreen -import com.nexters.boolti.presentation.screen.reservations.ReservationsScreen -import com.nexters.boolti.presentation.screen.showdetail.ShowDetailContentScreen -import com.nexters.boolti.presentation.screen.showdetail.ShowDetailScreen -import com.nexters.boolti.presentation.screen.showdetail.ShowImagesScreen -import com.nexters.boolti.presentation.screen.signout.SignoutScreen -import com.nexters.boolti.presentation.screen.ticket.detail.TicketDetailScreen -import com.nexters.boolti.presentation.screen.ticketing.TicketingScreen +import com.nexters.boolti.presentation.screen.accountsetting.accountSettingScreen +import com.nexters.boolti.presentation.screen.business.businessScreen +import com.nexters.boolti.presentation.screen.gift.giftScreen +import com.nexters.boolti.presentation.screen.giftcomplete.giftCompleteScreen +import com.nexters.boolti.presentation.screen.home.homeScreen +import com.nexters.boolti.presentation.screen.login.loginScreen +import com.nexters.boolti.presentation.screen.payment.paymentCompleteScreen +import com.nexters.boolti.presentation.screen.profile.profileScreen +import com.nexters.boolti.presentation.screen.profileedit.link.profileLinkEditScreen +import com.nexters.boolti.presentation.screen.profileedit.profile.profileEditScreen +import com.nexters.boolti.presentation.screen.qr.hostedShowScreen +import com.nexters.boolti.presentation.screen.qr.qrFullScreen +import com.nexters.boolti.presentation.screen.refund.refundScreen +import com.nexters.boolti.presentation.screen.report.reportScreen +import com.nexters.boolti.presentation.screen.reservations.reservationsScreen +import com.nexters.boolti.presentation.screen.showdetail.showDetailContentScreen +import com.nexters.boolti.presentation.screen.showdetail.showDetailScreen +import com.nexters.boolti.presentation.screen.showdetail.showImagesScreen +import com.nexters.boolti.presentation.screen.signout.signoutScreen +import com.nexters.boolti.presentation.screen.ticket.detail.ticketDetailScreen +import com.nexters.boolti.presentation.screen.ticketing.ticketingScreen import com.nexters.boolti.presentation.theme.BooltiTheme import com.nexters.boolti.presentation.util.SnackbarController import com.nexters.boolti.presentation.util.rememberNavControllerWithLog @@ -98,21 +98,21 @@ fun MainNavigation( navController = navController, startDestination = Home.route, ) { - HomeScreen(navigateTo = navController::navigateTo) - LoginScreen(popBackStack = navController::popBackStack) - SignoutScreen( + homeScreen(navigateTo = navController::navigateTo) + loginScreen(popBackStack = navController::popBackStack) + signoutScreen( navigateToHome = navController::navigateToHome, popBackStack = navController::popBackStack ) - ReservationsScreen( + reservationsScreen( navigateTo = navController::navigateTo, popBackStack = navController::popBackStack ) - ReservationDetailScreen( + reservationDetailScreen( navigateTo = navController::navigateTo, popBackStack = navController::popBackStack ) - RefundScreen(popBackStack = navController::popBackStack) + refundScreen(popBackStack = navController::popBackStack) navigation( route = "${ShowDetail.route}/{$showId}", @@ -125,27 +125,27 @@ fun MainNavigation( }, ), ) { - ShowDetailScreen( + showDetailScreen( navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, navigateToHome = navController::navigateToHome, getSharedViewModel = { entry -> entry.sharedViewModel(navController) } ) - ShowImagesScreen( + showImagesScreen( popBackStack = navController::popBackStack, getSharedViewModel = { entry -> entry.sharedViewModel(navController) } ) - ShowDetailContentScreen( + showDetailContentScreen( popBackStack = navController::popBackStack, getSharedViewModel = { entry -> entry.sharedViewModel(navController) } ) - ReportScreen( + reportScreen( navigateToHome = navController::navigateToHome, popBackStack = navController::popBackStack, ) } - TicketingScreen( + ticketingScreen( navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, ) @@ -161,28 +161,28 @@ fun MainNavigation( startDestination = "detail", arguments = MainDestination.TicketDetail.arguments, ) { - TicketDetailScreen( + ticketDetailScreen( navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, getSharedViewModel = { entry -> entry.sharedViewModel(navController) }, ) - QrFullScreen( + qrFullScreen( popBackStack = navController::popBackStack, getSharedViewModel = { entry -> entry.sharedViewModel(navController) }, ) } - addGiftScreen( + giftScreen( navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, ) - HostedShowScreen( + hostedShowScreen( onClickShow = onClickQrScan, popBackStack = navController::popBackStack, ) - PaymentCompleteScreen( + paymentCompleteScreen( navigateTo = navController::navigateTo, navigateByDeepLink = navController::navigate, popBackStack = navController::popBackStack, @@ -194,17 +194,17 @@ fun MainNavigation( }, navigateToHome = navController::navigateToHome, ) - addGiftCompleteScreen( + giftCompleteScreen( navigateTo = navController::navigateTo, navigateToHome = navController::navigateToHome, popBackStack = { navController.popBackStack(MainDestination.Gift.route, true) } ) - BusinessScreen(popBackStack = navController::popBackStack) - AccountSettingScreen( + businessScreen(popBackStack = navController::popBackStack) + accountSettingScreen( navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, ) - ProfileScreen( + profileScreen( navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, ) @@ -212,11 +212,11 @@ fun MainNavigation( route = "profileEditNavigation", startDestination = MainDestination.ProfileEdit.route, ) { - ProfileEditScreen( + profileEditScreen( navigateTo = navController::navigate, popBackStack = navController::popBackStack, ) - ProfileLinkEditScreen( + profileLinkEditScreen( onAddLink = { linkName, url -> navController.previousBackStackEntry ?.savedStateHandle diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt index 846722b3f..6a3f935be 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt @@ -4,7 +4,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination -fun NavGraphBuilder.AccountSettingScreen( +fun NavGraphBuilder.accountSettingScreen( navigateTo: (String) -> Unit, popBackStack: () -> Unit, ) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt index 8d8d6e9e5..ca6246a8d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt @@ -4,7 +4,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination -fun NavGraphBuilder.BusinessScreen( +fun NavGraphBuilder.businessScreen( popBackStack: () -> Unit, ) { composable( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt index 07bdf494d..923e4f8b0 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt @@ -5,7 +5,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination -fun NavGraphBuilder.addGiftScreen( +fun NavGraphBuilder.giftScreen( navigateTo: (String) -> Unit, popBackStack: () -> Unit, modifier: Modifier = Modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt index 5d43cfce5..aa1b8a86e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt @@ -4,7 +4,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination -fun NavGraphBuilder.addGiftCompleteScreen( +fun NavGraphBuilder.giftCompleteScreen( navigateTo: (String) -> Unit, navigateToHome: () -> Unit, popBackStack: () -> Unit, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index 5e349e72e..9a3bafb0d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -5,7 +5,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination -fun NavGraphBuilder.HomeScreen( +fun NavGraphBuilder.homeScreen( navigateTo: (String) -> Unit, modifier: Modifier = Modifier, ) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt index 42d4675a7..95385b4e5 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt @@ -1,12 +1,11 @@ package com.nexters.boolti.presentation.screen.login import androidx.compose.ui.Modifier -import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination -fun NavGraphBuilder.LoginScreen( +fun NavGraphBuilder.loginScreen( popBackStack: () -> Unit, modifier: Modifier = Modifier, ) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt index a4ee236d1..829257256 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt @@ -8,7 +8,7 @@ import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.reservationId import com.nexters.boolti.presentation.screen.showId -fun NavGraphBuilder.PaymentCompleteScreen( +fun NavGraphBuilder.paymentCompleteScreen( navigateTo: (String) -> Unit, navigateByDeepLink: (Uri) -> Unit, popBackStack: () -> Unit, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt index ef31c8a86..f7f63e093 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt @@ -5,7 +5,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination -fun NavGraphBuilder.ProfileScreen( +fun NavGraphBuilder.profileScreen( navigateTo: (String) -> Unit, popBackStack: () -> Unit, modifier: Modifier = Modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt index f4472a4ec..8334cee34 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt @@ -5,7 +5,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination -fun NavGraphBuilder.ProfileLinkEditScreen( +fun NavGraphBuilder.profileLinkEditScreen( onAddLink: (linkName: String, url: String) -> Unit, onEditLink: (id: String, linkName: String, url: String) -> Unit, onRemoveLink: (id: String) -> Unit, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt index 716acdc48..0df63fbf8 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt @@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.zip import java.util.UUID -fun NavGraphBuilder.ProfileEditScreen( +fun NavGraphBuilder.profileEditScreen( navigateTo: (String) -> Unit, popBackStack: () -> Unit, modifier: Modifier = Modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt index 4c998285c..a7df5d3db 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt @@ -5,7 +5,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination -fun NavGraphBuilder.HostedShowScreen( +fun NavGraphBuilder.hostedShowScreen( popBackStack: () -> Unit, onClickShow: (showId: String, showName: String) -> Unit, modifier: Modifier = Modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt index 4ae70449d..c2586fd5e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt @@ -54,7 +54,7 @@ import com.nexters.boolti.presentation.theme.Grey90 import com.nexters.boolti.presentation.theme.point4 import com.nexters.boolti.presentation.util.rememberQrBitmapPainter -fun NavGraphBuilder.QrFullScreen( +fun NavGraphBuilder.qrFullScreen( popBackStack: () -> Unit, getSharedViewModel: @Composable (NavBackStackEntry) -> TicketDetailViewModel, modifier: Modifier = Modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt index 23ec4db67..e8e8511ea 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt @@ -3,9 +3,8 @@ package com.nexters.boolti.presentation.screen.refund import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination -import com.nexters.boolti.presentation.screen.reservationId -fun NavGraphBuilder.RefundScreen( +fun NavGraphBuilder.refundScreen( popBackStack: () -> Unit, ) { composable( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt index 969e56573..da16216a4 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt @@ -4,7 +4,7 @@ import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable -fun NavGraphBuilder.ReportScreen( +fun NavGraphBuilder.reportScreen( navigateToHome: () -> Unit, popBackStack: () -> Unit, modifier: Modifier = Modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt index d4879aed9..d86e2edeb 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt @@ -4,7 +4,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination -fun NavGraphBuilder.ReservationsScreen( +fun NavGraphBuilder.reservationsScreen( navigateTo: (String) -> Unit, popBackStack: () -> Unit, ) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt index a9911c120..300fd9e82 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt @@ -6,7 +6,7 @@ import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable -fun NavGraphBuilder.ShowDetailContentScreen( +fun NavGraphBuilder.showDetailContentScreen( popBackStack: () -> Unit, getSharedViewModel: @Composable (NavBackStackEntry) -> ShowDetailViewModel, modifier: Modifier = Modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt index f1cb78901..ac1f6b43f 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt @@ -7,7 +7,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination -fun NavGraphBuilder.ShowDetailScreen( +fun NavGraphBuilder.showDetailScreen( navigateTo: (String) -> Unit, popBackStack: () -> Unit, navigateToHome: () -> Unit, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt index ecfee92ca..d7fb9f722 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt @@ -7,7 +7,7 @@ import androidx.navigation.NavType import androidx.navigation.compose.composable import androidx.navigation.navArgument -fun NavGraphBuilder.ShowImagesScreen( +fun NavGraphBuilder.showImagesScreen( popBackStack: () -> Unit, getSharedViewModel: @Composable (NavBackStackEntry) -> ShowDetailViewModel, ) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt index 011753c39..8d37dedae 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt @@ -4,7 +4,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination -fun NavGraphBuilder.SignoutScreen( +fun NavGraphBuilder.signoutScreen( navigateToHome: () -> Unit, popBackStack: () -> Unit, ) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt index 25de2130d..435d5296c 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt @@ -115,7 +115,7 @@ import com.nexters.boolti.presentation.util.rememberQrBitmapPainter import kotlinx.coroutines.launch import java.time.LocalDate -fun NavGraphBuilder.TicketDetailScreen( +fun NavGraphBuilder.ticketDetailScreen( navigateTo: (String) -> Unit, popBackStack: () -> Unit, getSharedViewModel: @Composable (NavBackStackEntry) -> TicketDetailViewModel, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt index 7f11d2bed..8ed753b50 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt @@ -9,7 +9,7 @@ import com.nexters.boolti.presentation.screen.salesTicketId import com.nexters.boolti.presentation.screen.showId import com.nexters.boolti.presentation.screen.ticketCount -fun NavGraphBuilder.TicketingScreen( +fun NavGraphBuilder.ticketingScreen( navigateTo: (String) -> Unit, popBackStack: () -> Unit, modifier: Modifier = Modifier, From 79027e83e57fedb1a11e942bd413e190900922d4 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 21:07:22 +0900 Subject: [PATCH 09/90] =?UTF-8?q?refactor=20:=20=EB=82=B4=EB=B9=84?= =?UTF-8?q?=EA=B2=8C=EC=9D=B4=EC=85=98=20=EC=BB=B4=ED=8F=AC=EC=A0=80?= =?UTF-8?q?=EB=B8=94=20=ED=95=A8=EC=88=98=20=EB=AA=85=20=EC=B9=B4=EB=A9=9C?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=8A=A4=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(Ho?= =?UTF-8?q?me)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/home/HomeScreen.kt | 12 ++++++------ .../boolti/presentation/screen/my/MyNavigation.kt | 2 +- .../presentation/screen/show/ShowNavigation.kt | 2 +- .../presentation/screen/ticket/TicketNavigation.kt | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt index d7b5c761e..eb720bfae 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt @@ -33,11 +33,11 @@ import androidx.navigation.compose.currentBackStackEntryAsState import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.extension.requireActivity import com.nexters.boolti.presentation.screen.LocalSnackbarController -import com.nexters.boolti.presentation.screen.my.addMy +import com.nexters.boolti.presentation.screen.my.myScreen import com.nexters.boolti.presentation.screen.navigation.HomeRoute import com.nexters.boolti.presentation.screen.navigation.homeRoutes -import com.nexters.boolti.presentation.screen.show.addShow -import com.nexters.boolti.presentation.screen.ticket.addTicket +import com.nexters.boolti.presentation.screen.show.showScreen +import com.nexters.boolti.presentation.screen.ticket.ticketScreen import com.nexters.boolti.presentation.theme.Grey10 import com.nexters.boolti.presentation.theme.Grey50 import com.nexters.boolti.presentation.theme.Grey85 @@ -117,18 +117,18 @@ fun HomeScreen( navController = navController, startDestination = HomeRoute.Show, ) { - addShow( + showScreen( navigateToShowDetail = navigateToShowDetail, navigateToBusiness = navigateToBusiness, ) - addTicket( + ticketScreen( isLoggedIn = isLoggedIn, navigateToLogin = navigateToLogin, navigateToTicketDetail = navigateToTicketDetail, ) - addMy( + myScreen( navigateToLogin = navigateToLogin, navigateToAccountSetting = navigateToAccountSetting, navigateToReservations = navigateToReservations, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt index 56349cfe4..9a74cc987 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt @@ -4,7 +4,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.navigation.HomeRoute -fun NavGraphBuilder.addMy( +fun NavGraphBuilder.myScreen( navigateToLogin: () -> Unit, navigateToAccountSetting: () -> Unit, navigateToReservations: () -> Unit, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt index e8532fc3b..eb682204c 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt @@ -6,7 +6,7 @@ import androidx.navigation.compose.composable import androidx.navigation.navDeepLink import com.nexters.boolti.presentation.screen.navigation.HomeRoute -fun NavGraphBuilder.addShow( +fun NavGraphBuilder.showScreen( navigateToShowDetail: (showId: String) -> Unit, navigateToBusiness: () -> Unit, ) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt index e1c4293aa..3fdc490e4 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt @@ -6,7 +6,7 @@ import androidx.navigation.compose.composable import androidx.navigation.navDeepLink import com.nexters.boolti.presentation.screen.navigation.HomeRoute -fun NavGraphBuilder.addTicket( +fun NavGraphBuilder.ticketScreen( isLoggedIn: Boolean?, navigateToTicketDetail: (String) -> Unit, navigateToLogin: () -> Unit, From 6a16ac7b1715d5662a391f2ee934aa110cde4d16 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 21:15:35 +0900 Subject: [PATCH 10/90] =?UTF-8?q?refactor=20:=20NavConroller=EB=A5=BC=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=EB=A1=9C=20=EC=A0=84?= =?UTF-8?q?=EB=8B=AC=20-=20Main=20=ED=99=94=EB=A9=B4=20refactoring=20?= =?UTF-8?q?=EC=82=AC=EC=A0=84=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReservationDetailNavigation.kt | 2 + .../boolti/presentation/screen/Main.kt | 38 +++++++++++++++++-- .../AccountSettingNavigation.kt | 2 + .../screen/business/BusinessNavigation.kt | 2 + .../screen/gift/GiftNavigation.kt | 2 + .../giftcomplete/GiftCompleteNavigation.kt | 2 + .../screen/home/HomeNavigation.kt | 2 + .../screen/login/LoginNavigation.kt | 2 + .../screen/payment/PaymentNavigation.kt | 2 + .../screen/profile/ProfileNavigation.kt | 2 + .../link/ProfileLinkEditNavigation.kt | 2 + .../profile/ProfileEditNavigation.kt | 2 + .../screen/qr/HostedShowNavigation.kt | 2 + .../presentation/screen/qr/QrFullScreen.kt | 2 + .../screen/refund/RefundNavigation.kt | 2 + .../screen/report/ReportNavigation.kt | 2 + .../reservations/ReservationsNavigation.kt | 2 + .../showdetail/ShowDetailContentNavigation.kt | 2 + .../screen/showdetail/ShowDetailNavigation.kt | 2 + .../screen/showdetail/ShowImagesNavigation.kt | 2 + .../screen/signout/SignoutNavigation.kt | 2 + .../ticket/detail/TicketDetailScreen.kt | 2 + .../screen/ticketing/TicketingNavigation.kt | 2 + 23 files changed, 78 insertions(+), 4 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt index a06888800..7943166a7 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt @@ -1,10 +1,12 @@ package com.nexters.boolti.presentation.reservationdetail import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.reservationDetailScreen( + navController: NavHostController, navigateTo: (String) -> Unit, popBackStack: () -> Unit, ) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index f0dc0fe49..45b94c1f9 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -98,21 +98,33 @@ fun MainNavigation( navController = navController, startDestination = Home.route, ) { - homeScreen(navigateTo = navController::navigateTo) - loginScreen(popBackStack = navController::popBackStack) + homeScreen( + navController = navController, + navigateTo = navController::navigateTo + ) + loginScreen( + navController = navController, + popBackStack = navController::popBackStack + ) signoutScreen( + navController = navController, navigateToHome = navController::navigateToHome, popBackStack = navController::popBackStack ) reservationsScreen( + navController = navController, navigateTo = navController::navigateTo, popBackStack = navController::popBackStack ) reservationDetailScreen( + navController = navController, navigateTo = navController::navigateTo, popBackStack = navController::popBackStack ) - refundScreen(popBackStack = navController::popBackStack) + refundScreen( + navController = navController, + popBackStack = navController::popBackStack + ) navigation( route = "${ShowDetail.route}/{$showId}", @@ -126,26 +138,31 @@ fun MainNavigation( ), ) { showDetailScreen( + navController = navController, navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, navigateToHome = navController::navigateToHome, getSharedViewModel = { entry -> entry.sharedViewModel(navController) } ) showImagesScreen( + navController = navController, popBackStack = navController::popBackStack, getSharedViewModel = { entry -> entry.sharedViewModel(navController) } ) showDetailContentScreen( + navController = navController, popBackStack = navController::popBackStack, getSharedViewModel = { entry -> entry.sharedViewModel(navController) } ) reportScreen( + navController = navController, navigateToHome = navController::navigateToHome, popBackStack = navController::popBackStack, ) } ticketingScreen( + navController = navController, navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, ) @@ -162,27 +179,32 @@ fun MainNavigation( arguments = MainDestination.TicketDetail.arguments, ) { ticketDetailScreen( + navController = navController, navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, getSharedViewModel = { entry -> entry.sharedViewModel(navController) }, ) qrFullScreen( + navController = navController, popBackStack = navController::popBackStack, getSharedViewModel = { entry -> entry.sharedViewModel(navController) }, ) } giftScreen( + navController = navController, navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, ) hostedShowScreen( + navController = navController, onClickShow = onClickQrScan, popBackStack = navController::popBackStack, ) paymentCompleteScreen( + navController = navController, navigateTo = navController::navigateTo, navigateByDeepLink = navController::navigate, popBackStack = navController::popBackStack, @@ -195,16 +217,22 @@ fun MainNavigation( navigateToHome = navController::navigateToHome, ) giftCompleteScreen( + navController = navController, navigateTo = navController::navigateTo, navigateToHome = navController::navigateToHome, popBackStack = { navController.popBackStack(MainDestination.Gift.route, true) } ) - businessScreen(popBackStack = navController::popBackStack) + businessScreen( + navController = navController, + popBackStack = navController::popBackStack + ) accountSettingScreen( + navController = navController, navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, ) profileScreen( + navController = navController, navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, ) @@ -213,10 +241,12 @@ fun MainNavigation( startDestination = MainDestination.ProfileEdit.route, ) { profileEditScreen( + navController = navController, navigateTo = navController::navigate, popBackStack = navController::popBackStack, ) profileLinkEditScreen( + navController = navController, onAddLink = { linkName, url -> navController.previousBackStackEntry ?.savedStateHandle diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt index 6a3f935be..8abd61997 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt @@ -1,10 +1,12 @@ package com.nexters.boolti.presentation.screen.accountsetting import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.accountSettingScreen( + navController: NavHostController, navigateTo: (String) -> Unit, popBackStack: () -> Unit, ) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt index ca6246a8d..bd0f423bb 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt @@ -1,10 +1,12 @@ package com.nexters.boolti.presentation.screen.business import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.businessScreen( + navController: NavHostController, popBackStack: () -> Unit, ) { composable( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt index 923e4f8b0..66f1771b5 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt @@ -2,10 +2,12 @@ package com.nexters.boolti.presentation.screen.gift import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.giftScreen( + navController: NavHostController, navigateTo: (String) -> Unit, popBackStack: () -> Unit, modifier: Modifier = Modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt index aa1b8a86e..1c00f3898 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt @@ -1,10 +1,12 @@ package com.nexters.boolti.presentation.screen.giftcomplete import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.giftCompleteScreen( + navController: NavHostController, navigateTo: (String) -> Unit, navigateToHome: () -> Unit, popBackStack: () -> Unit, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index 9a3bafb0d..7e4dbbadd 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -2,10 +2,12 @@ package com.nexters.boolti.presentation.screen.home import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.homeScreen( + navController: NavHostController, navigateTo: (String) -> Unit, modifier: Modifier = Modifier, ) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt index 95385b4e5..153be3c1b 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt @@ -2,10 +2,12 @@ package com.nexters.boolti.presentation.screen.login import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.loginScreen( + navController: NavHostController, popBackStack: () -> Unit, modifier: Modifier = Modifier, ) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt index 829257256..ee846c875 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt @@ -3,12 +3,14 @@ package com.nexters.boolti.presentation.screen.payment import android.net.Uri import androidx.core.net.toUri import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.reservationId import com.nexters.boolti.presentation.screen.showId fun NavGraphBuilder.paymentCompleteScreen( + navController: NavHostController, navigateTo: (String) -> Unit, navigateByDeepLink: (Uri) -> Unit, popBackStack: () -> Unit, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt index f7f63e093..00ca990a7 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt @@ -2,10 +2,12 @@ package com.nexters.boolti.presentation.screen.profile import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.profileScreen( + navController: NavHostController, navigateTo: (String) -> Unit, popBackStack: () -> Unit, modifier: Modifier = Modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt index 8334cee34..28c6fe8b2 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt @@ -2,10 +2,12 @@ package com.nexters.boolti.presentation.screen.profileedit.link import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.profileLinkEditScreen( + navController: NavHostController, onAddLink: (linkName: String, url: String) -> Unit, onEditLink: (id: String, linkName: String, url: String) -> Unit, onRemoveLink: (id: String) -> Unit, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt index 0df63fbf8..67ecc6ce5 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt @@ -2,6 +2,7 @@ package com.nexters.boolti.presentation.screen.profileedit.profile import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.domain.model.Link import com.nexters.boolti.presentation.screen.MainDestination @@ -11,6 +12,7 @@ import kotlinx.coroutines.flow.zip import java.util.UUID fun NavGraphBuilder.profileEditScreen( + navController: NavHostController, navigateTo: (String) -> Unit, popBackStack: () -> Unit, modifier: Modifier = Modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt index a7df5d3db..5e529e205 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt @@ -2,10 +2,12 @@ package com.nexters.boolti.presentation.screen.qr import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.hostedShowScreen( + navController: NavHostController, popBackStack: () -> Unit, onClickShow: (showId: String, showName: String) -> Unit, modifier: Modifier = Modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt index c2586fd5e..a0150a688 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt @@ -40,6 +40,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.domain.model.TicketState import com.nexters.boolti.presentation.R @@ -55,6 +56,7 @@ import com.nexters.boolti.presentation.theme.point4 import com.nexters.boolti.presentation.util.rememberQrBitmapPainter fun NavGraphBuilder.qrFullScreen( + navController: NavHostController, popBackStack: () -> Unit, getSharedViewModel: @Composable (NavBackStackEntry) -> TicketDetailViewModel, modifier: Modifier = Modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt index e8e8511ea..3b0f75026 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt @@ -1,10 +1,12 @@ package com.nexters.boolti.presentation.screen.refund import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.refundScreen( + navController: NavHostController, popBackStack: () -> Unit, ) { composable( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt index da16216a4..6e98d6dc5 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt @@ -2,9 +2,11 @@ package com.nexters.boolti.presentation.screen.report import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable fun NavGraphBuilder.reportScreen( + navController: NavHostController, navigateToHome: () -> Unit, popBackStack: () -> Unit, modifier: Modifier = Modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt index d86e2edeb..ad0cd3e5d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt @@ -1,10 +1,12 @@ package com.nexters.boolti.presentation.screen.reservations import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.reservationsScreen( + navController: NavHostController, navigateTo: (String) -> Unit, popBackStack: () -> Unit, ) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt index 300fd9e82..8d8352983 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt @@ -4,9 +4,11 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable fun NavGraphBuilder.showDetailContentScreen( + navController: NavHostController, popBackStack: () -> Unit, getSharedViewModel: @Composable (NavBackStackEntry) -> ShowDetailViewModel, modifier: Modifier = Modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt index ac1f6b43f..a14dcc4f8 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt @@ -4,10 +4,12 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.showDetailScreen( + navController: NavHostController, navigateTo: (String) -> Unit, popBackStack: () -> Unit, navigateToHome: () -> Unit, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt index d7fb9f722..02aa2dd22 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt @@ -3,11 +3,13 @@ package com.nexters.boolti.presentation.screen.showdetail import androidx.compose.runtime.Composable import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.composable import androidx.navigation.navArgument fun NavGraphBuilder.showImagesScreen( + navController: NavHostController, popBackStack: () -> Unit, getSharedViewModel: @Composable (NavBackStackEntry) -> ShowDetailViewModel, ) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt index 8d37dedae..4cb1ed916 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt @@ -1,10 +1,12 @@ package com.nexters.boolti.presentation.screen.signout import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.signoutScreen( + navController: NavHostController, navigateToHome: () -> Unit, popBackStack: () -> Unit, ) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt index 435d5296c..c688129ab 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt @@ -83,6 +83,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import coil.compose.AsyncImage import com.nexters.boolti.domain.model.TicketGroup @@ -116,6 +117,7 @@ import kotlinx.coroutines.launch import java.time.LocalDate fun NavGraphBuilder.ticketDetailScreen( + navController: NavHostController, navigateTo: (String) -> Unit, popBackStack: () -> Unit, getSharedViewModel: @Composable (NavBackStackEntry) -> TicketDetailViewModel, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt index 8ed753b50..a38217736 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt @@ -2,6 +2,7 @@ package com.nexters.boolti.presentation.screen.ticketing import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.isInviteTicket @@ -10,6 +11,7 @@ import com.nexters.boolti.presentation.screen.showId import com.nexters.boolti.presentation.screen.ticketCount fun NavGraphBuilder.ticketingScreen( + navController: NavHostController, navigateTo: (String) -> Unit, popBackStack: () -> Unit, modifier: Modifier = Modifier, From 5999aaa3922f58dee5f82045a7b5e84beade32a6 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 21:19:53 +0900 Subject: [PATCH 11/90] =?UTF-8?q?refactor=20:=20Home=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=20route=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nexters/boolti/presentation/extension/NavController.kt | 4 ++-- .../main/java/com/nexters/boolti/presentation/screen/Main.kt | 3 ++- .../nexters/boolti/presentation/screen/MainDestination.kt | 1 - .../boolti/presentation/screen/home/HomeNavigation.kt | 5 ++--- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/extension/NavController.kt b/presentation/src/main/java/com/nexters/boolti/presentation/extension/NavController.kt index efb981848..d2a597f70 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/extension/NavController.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/extension/NavController.kt @@ -1,12 +1,12 @@ package com.nexters.boolti.presentation.extension import androidx.navigation.NavController -import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavController.navigateToHome() { popBackStack(graph.startDestinationId, true) try { - navigate(MainDestination.Home.route) + navigate(MainRoute.Home) } catch (e: IllegalArgumentException) { navigate(graph.startDestinationId) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 45b94c1f9..7b5af5c2e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -32,6 +32,7 @@ import com.nexters.boolti.presentation.screen.gift.giftScreen import com.nexters.boolti.presentation.screen.giftcomplete.giftCompleteScreen import com.nexters.boolti.presentation.screen.home.homeScreen import com.nexters.boolti.presentation.screen.login.loginScreen +import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.payment.paymentCompleteScreen import com.nexters.boolti.presentation.screen.profile.profileScreen import com.nexters.boolti.presentation.screen.profileedit.link.profileLinkEditScreen @@ -96,7 +97,7 @@ fun MainNavigation( NavHost( modifier = modifier, navController = navController, - startDestination = Home.route, + startDestination = MainRoute.Home, ) { homeScreen( navController = navController, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index 5d5f6e9e4..37119bf69 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -5,7 +5,6 @@ import androidx.navigation.navArgument import com.nexters.boolti.domain.model.Link sealed class MainDestination(val route: String) { - data object Home : MainDestination(route = "home") data object ShowDetail : MainDestination(route = "show") { val arguments = listOf(navArgument(showId) { type = NavType.StringType }) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index 7e4dbbadd..e2929baa9 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -5,15 +5,14 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.homeScreen( navController: NavHostController, navigateTo: (String) -> Unit, modifier: Modifier = Modifier, ) { - composable( - route = MainDestination.Home.route, - ) { + composable { HomeScreen( modifier = modifier, navigateToShowDetail = { navigateTo("${MainDestination.ShowDetail.route}/$it") }, From 6f29b6f634b8a2b75d9630a0fe8b715a38f590ae Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 21:26:27 +0900 Subject: [PATCH 12/90] =?UTF-8?q?refactor=20:=20Login=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=20route=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/nexters/boolti/presentation/screen/Main.kt | 1 - .../nexters/boolti/presentation/screen/MainDestination.kt | 1 - .../boolti/presentation/screen/home/HomeNavigation.kt | 2 +- .../boolti/presentation/screen/login/LoginNavigation.kt | 5 ++--- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 7b5af5c2e..95508c72e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -24,7 +24,6 @@ import androidx.navigation.navDeepLink import com.nexters.boolti.presentation.component.ToastSnackbarHost import com.nexters.boolti.presentation.extension.navigateToHome import com.nexters.boolti.presentation.reservationdetail.reservationDetailScreen -import com.nexters.boolti.presentation.screen.MainDestination.Home import com.nexters.boolti.presentation.screen.MainDestination.ShowDetail import com.nexters.boolti.presentation.screen.accountsetting.accountSettingScreen import com.nexters.boolti.presentation.screen.business.businessScreen diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index 37119bf69..18c14dd8f 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -84,7 +84,6 @@ sealed class MainDestination(val route: String) { data object HostedShows : MainDestination(route = "hostedShows") data object SignOut : MainDestination(route = "signout") - data object Login : MainDestination(route = "login") data object Business : MainDestination(route = "business") data object AccountSetting : MainDestination(route = "accountSetting") data object Profile : MainDestination(route = "profile?userCode={$userCode}") { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index e2929baa9..1d1461e5a 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -22,7 +22,7 @@ fun NavGraphBuilder.homeScreen( navigateToReservations = { navigateTo(MainDestination.Reservations.route) }, navigateToProfile = { navigateTo(MainDestination.Profile.createRoute()) }, navigateToBusiness = { navigateTo(MainDestination.Business.route) }, - navigateToLogin = { navigateTo(MainDestination.Login.route) }, + navigateToLogin = { navController.navigate(MainRoute.Login) }, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt index 153be3c1b..ff186bc43 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt @@ -5,15 +5,14 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.loginScreen( navController: NavHostController, popBackStack: () -> Unit, modifier: Modifier = Modifier, ) { - composable( - route = MainDestination.Login.route, - ) { + composable { LoginScreen( modifier = modifier, onBackPressed = popBackStack From 52d0c2809a1df806a876d08fa3e70ec7ab9c0c35 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 21:28:42 +0900 Subject: [PATCH 13/90] =?UTF-8?q?refactor=20:=20sign=20out=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20route=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/nexters/boolti/presentation/screen/Main.kt | 1 - .../nexters/boolti/presentation/screen/MainDestination.kt | 1 - .../screen/accountsetting/AccountSettingNavigation.kt | 4 ++-- .../boolti/presentation/screen/signout/SignoutNavigation.kt | 5 ++--- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 95508c72e..395e7bb58 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -228,7 +228,6 @@ fun MainNavigation( ) accountSettingScreen( navController = navController, - navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, ) profileScreen( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index 18c14dd8f..b3125b200 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -83,7 +83,6 @@ sealed class MainDestination(val route: String) { data object HostedShows : MainDestination(route = "hostedShows") - data object SignOut : MainDestination(route = "signout") data object Business : MainDestination(route = "business") data object AccountSetting : MainDestination(route = "accountSetting") data object Profile : MainDestination(route = "profile?userCode={$userCode}") { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt index 8abd61997..433cef7ee 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt @@ -4,10 +4,10 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.accountSettingScreen( navController: NavHostController, - navigateTo: (String) -> Unit, popBackStack: () -> Unit, ) { composable( @@ -15,7 +15,7 @@ fun NavGraphBuilder.accountSettingScreen( ) { AccountSettingScreen( navigateBack = popBackStack, - onClickResign = { navigateTo(MainDestination.SignOut.route) }, + onClickResign = { navController.navigate(MainRoute.SignOut) }, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt index 4cb1ed916..054d3742b 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt @@ -4,15 +4,14 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.signoutScreen( navController: NavHostController, navigateToHome: () -> Unit, popBackStack: () -> Unit, ) { - composable( - route = MainDestination.SignOut.route, - ) { + composable { SignoutScreen( navigateToHome = navigateToHome, navigateBack = popBackStack, From f446939fcce2e1fa9a9cd283fd5ca102d8e73a02 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 21:31:36 +0900 Subject: [PATCH 14/90] =?UTF-8?q?refactor=20:=20reservations=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20route=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nexters/boolti/presentation/screen/MainDestination.kt | 1 - .../boolti/presentation/screen/home/HomeNavigation.kt | 2 +- .../screen/reservations/ReservationsNavigation.kt | 5 ++--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index b3125b200..3d3f3d33f 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -56,7 +56,6 @@ sealed class MainDestination(val route: String) { data object Qr : MainDestination(route = "qr") - data object Reservations : MainDestination(route = "reservations") data object ReservationDetail : MainDestination(route = "reservations/{reservationId}?isGift={isGift}") { val arguments = listOf( navArgument("reservationId") { type = NavType.StringType }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index 1d1461e5a..ee2b88926 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -19,7 +19,7 @@ fun NavGraphBuilder.homeScreen( navigateToTicketDetail = { navigateTo("${MainDestination.TicketDetail.route}/$it") }, navigateToQrScan = { navigateTo(MainDestination.HostedShows.route) }, navigateToAccountSetting = { navigateTo(MainDestination.AccountSetting.route) }, - navigateToReservations = { navigateTo(MainDestination.Reservations.route) }, + navigateToReservations = { navController.navigate(MainRoute.Reservations) }, navigateToProfile = { navigateTo(MainDestination.Profile.createRoute()) }, navigateToBusiness = { navigateTo(MainDestination.Business.route) }, navigateToLogin = { navController.navigate(MainRoute.Login) }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt index ad0cd3e5d..55ee141c7 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt @@ -4,15 +4,14 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.reservationsScreen( navController: NavHostController, navigateTo: (String) -> Unit, popBackStack: () -> Unit, ) { - composable( - route = MainDestination.Reservations.route, - ) { + composable { ReservationsScreen( onBackPressed = popBackStack, navigateToDetail = { id, isGift -> From 2a06282f2b02b397695f75bc10a3fa20786f7d8d Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 21:36:39 +0900 Subject: [PATCH 15/90] =?UTF-8?q?refactor=20:=20reservationDetail=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20route=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reservationdetail/ReservationDetailNavigation.kt | 6 ++---- .../com/nexters/boolti/presentation/screen/Main.kt | 2 -- .../boolti/presentation/screen/MainDestination.kt | 12 ------------ .../screen/giftcomplete/GiftCompleteNavigation.kt | 8 ++++---- .../presentation/screen/payment/PaymentNavigation.kt | 8 +++++++- .../screen/reservations/ReservationsNavigation.kt | 9 ++++++--- 6 files changed, 19 insertions(+), 26 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt index 7943166a7..31532833a 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt @@ -4,16 +4,14 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.reservationDetailScreen( navController: NavHostController, navigateTo: (String) -> Unit, popBackStack: () -> Unit, ) { - composable( - route = MainDestination.ReservationDetail.route, - arguments = MainDestination.ReservationDetail.arguments, - ) { + composable { ReservationDetailScreen( onBackPressed = popBackStack, navigateToRefund = { id, isGift -> diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 395e7bb58..40b4db6c6 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -113,7 +113,6 @@ fun MainNavigation( ) reservationsScreen( navController = navController, - navigateTo = navController::navigateTo, popBackStack = navController::popBackStack ) reservationDetailScreen( @@ -218,7 +217,6 @@ fun MainNavigation( ) giftCompleteScreen( navController = navController, - navigateTo = navController::navigateTo, navigateToHome = navController::navigateToHome, popBackStack = { navController.popBackStack(MainDestination.Gift.route, true) } ) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index 3d3f3d33f..fe1319927 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -56,18 +56,6 @@ sealed class MainDestination(val route: String) { data object Qr : MainDestination(route = "qr") - data object ReservationDetail : MainDestination(route = "reservations/{reservationId}?isGift={isGift}") { - val arguments = listOf( - navArgument("reservationId") { type = NavType.StringType }, - navArgument("isGift") { type = NavType.BoolType }, - ) - - fun createRoute( - id: String, - isGift: Boolean = false, - ): String = "reservations/$id?isGift=$isGift" - } - data object Refund : MainDestination(route = "refund/{reservationId}?isGift={isGift}") { val arguments = listOf( navArgument("reservationId") { type = NavType.StringType }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt index 1c00f3898..5aa23d895 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt @@ -4,10 +4,10 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.giftCompleteScreen( navController: NavHostController, - navigateTo: (String) -> Unit, navigateToHome: () -> Unit, popBackStack: () -> Unit, ) { @@ -18,9 +18,9 @@ fun NavGraphBuilder.giftCompleteScreen( onClickClose = popBackStack, onClickHome = navigateToHome, navigateToReservation = { reservation -> - navigateTo( - MainDestination.ReservationDetail.createRoute( - id = reservation.id, + navController.navigate( + MainRoute.ReservationDetail( + reservationId = reservation.id, isGift = true, ) ) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt index ee846c875..9af738f30 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt @@ -6,6 +6,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.reservationId import com.nexters.boolti.presentation.screen.showId @@ -31,7 +32,12 @@ fun NavGraphBuilder.paymentCompleteScreen( } ?: popBackStack() }, navigateToReservation = { reservation -> - navigateTo(MainDestination.ReservationDetail.createRoute(id = reservation.id)) + navController.navigate( + MainRoute.ReservationDetail( + reservationId = reservation.id, + isGift = false, + ) + ) }, navigateToTicketDetail = { reservation -> navigateByDeepLink("https://app.boolti.in/tickets/${reservation.id}".toUri()) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt index 55ee141c7..3c0d8c95c 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt @@ -3,19 +3,22 @@ package com.nexters.boolti.presentation.screen.reservations import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable -import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.reservationsScreen( navController: NavHostController, - navigateTo: (String) -> Unit, popBackStack: () -> Unit, ) { composable { ReservationsScreen( onBackPressed = popBackStack, navigateToDetail = { id, isGift -> - navigateTo(MainDestination.ReservationDetail.createRoute(id = id, isGift = isGift)) + navController.navigate( + MainRoute.ReservationDetail( + reservationId = id, + isGift = isGift + ) + ) }) } } From 0c37eb45de7995ea01c9e0185d4a785e409a7c27 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 21:41:13 +0900 Subject: [PATCH 16/90] =?UTF-8?q?refactor=20:=20refund=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20route=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reservationdetail/ReservationDetailNavigation.kt | 4 ++-- .../com/nexters/boolti/presentation/screen/Main.kt | 1 - .../boolti/presentation/screen/MainDestination.kt | 12 ------------ .../presentation/screen/refund/RefundNavigation.kt | 11 +++++------ 4 files changed, 7 insertions(+), 21 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt index 31532833a..c7de8f073 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt @@ -5,17 +5,17 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute +import com.nexters.boolti.presentation.screen.reservationId fun NavGraphBuilder.reservationDetailScreen( navController: NavHostController, - navigateTo: (String) -> Unit, popBackStack: () -> Unit, ) { composable { ReservationDetailScreen( onBackPressed = popBackStack, navigateToRefund = { id, isGift -> - navigateTo(MainDestination.Refund.createRoute(id = id, isGift = isGift)) + navController.navigate(MainRoute.Refund(reservationId = id, isGift = isGift)) }, ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 40b4db6c6..ee9d2de3d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -117,7 +117,6 @@ fun MainNavigation( ) reservationDetailScreen( navController = navController, - navigateTo = navController::navigateTo, popBackStack = navController::popBackStack ) refundScreen( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index fe1319927..ac71a5c91 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -56,18 +56,6 @@ sealed class MainDestination(val route: String) { data object Qr : MainDestination(route = "qr") - data object Refund : MainDestination(route = "refund/{reservationId}?isGift={isGift}") { - val arguments = listOf( - navArgument("reservationId") { type = NavType.StringType }, - navArgument("isGift") { type = NavType.BoolType }, - ) - - fun createRoute( - id: String, - isGift: Boolean, - ): String = "refund/$id?isGift=$isGift" - } - data object HostedShows : MainDestination(route = "hostedShows") data object Business : MainDestination(route = "business") diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt index 3b0f75026..3eabc1c89 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt @@ -3,20 +3,19 @@ package com.nexters.boolti.presentation.screen.refund import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import androidx.navigation.toRoute import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.refundScreen( navController: NavHostController, popBackStack: () -> Unit, ) { - composable( - route = MainDestination.Refund.route, - arguments = MainDestination.Refund.arguments, - ) { entry -> - val isGift = entry.arguments?.getBoolean("isGift") + composable { entry -> + val route = entry.toRoute() RefundScreen( - isGift = isGift ?: false, + isGift = route.isGift, onBackPressed = popBackStack, ) } From 16bb38bccca9fe38f73c2d890578a978865e8901 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 21:50:00 +0900 Subject: [PATCH 17/90] =?UTF-8?q?refactor=20:=20ShowDetail=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20route=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/Main.kt | 15 +++--------- .../presentation/screen/MainDestination.kt | 4 ---- .../screen/home/HomeNavigation.kt | 2 +- .../screen/navigation/ShowRoute.kt | 9 ++++++++ .../screen/payment/PaymentNavigation.kt | 6 ++--- .../screen/showdetail/ShowDetailNavigation.kt | 5 ++-- .../ticket/detail/TicketDetailScreen.kt | 23 +++++++++++++++---- 7 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ShowRoute.kt diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index ee9d2de3d..9f63712d3 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -24,7 +24,6 @@ import androidx.navigation.navDeepLink import com.nexters.boolti.presentation.component.ToastSnackbarHost import com.nexters.boolti.presentation.extension.navigateToHome import com.nexters.boolti.presentation.reservationdetail.reservationDetailScreen -import com.nexters.boolti.presentation.screen.MainDestination.ShowDetail import com.nexters.boolti.presentation.screen.accountsetting.accountSettingScreen import com.nexters.boolti.presentation.screen.business.businessScreen import com.nexters.boolti.presentation.screen.gift.giftScreen @@ -32,6 +31,7 @@ import com.nexters.boolti.presentation.screen.giftcomplete.giftCompleteScreen import com.nexters.boolti.presentation.screen.home.homeScreen import com.nexters.boolti.presentation.screen.login.loginScreen import com.nexters.boolti.presentation.screen.navigation.MainRoute +import com.nexters.boolti.presentation.screen.navigation.ShowRoute import com.nexters.boolti.presentation.screen.payment.paymentCompleteScreen import com.nexters.boolti.presentation.screen.profile.profileScreen import com.nexters.boolti.presentation.screen.profileedit.link.profileLinkEditScreen @@ -124,10 +124,8 @@ fun MainNavigation( popBackStack = navController::popBackStack ) - navigation( - route = "${ShowDetail.route}/{$showId}", - startDestination = "detail", - arguments = ShowDetail.arguments, + navigation( + startDestination = ShowRoute.Detail, deepLinks = listOf( navDeepLink { uriPattern = "https://preview.boolti.in/show/{$showId}" @@ -203,15 +201,8 @@ fun MainNavigation( paymentCompleteScreen( navController = navController, - navigateTo = navController::navigateTo, navigateByDeepLink = navController::navigate, popBackStack = navController::popBackStack, - popInclusiveBackStack = { route -> - navController.popBackStack( - route = route, - inclusive = true, - ) - }, navigateToHome = navController::navigateToHome, ) giftCompleteScreen( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index ac71a5c91..e0ca7e245 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -5,10 +5,6 @@ import androidx.navigation.navArgument import com.nexters.boolti.domain.model.Link sealed class MainDestination(val route: String) { - data object ShowDetail : MainDestination(route = "show") { - val arguments = listOf(navArgument(showId) { type = NavType.StringType }) - } - data object Ticketing : MainDestination(route = "ticketing") { val arguments = listOf( navArgument(showId) { type = NavType.StringType }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index ee2b88926..818069a5b 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -15,7 +15,7 @@ fun NavGraphBuilder.homeScreen( composable { HomeScreen( modifier = modifier, - navigateToShowDetail = { navigateTo("${MainDestination.ShowDetail.route}/$it") }, + navigateToShowDetail = { navController.navigate(MainRoute.ShowDetail(showId = it)) }, navigateToTicketDetail = { navigateTo("${MainDestination.TicketDetail.route}/$it") }, navigateToQrScan = { navigateTo(MainDestination.HostedShows.route) }, navigateToAccountSetting = { navigateTo(MainDestination.AccountSetting.route) }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ShowRoute.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ShowRoute.kt new file mode 100644 index 000000000..8cbd3bf90 --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ShowRoute.kt @@ -0,0 +1,9 @@ +package com.nexters.boolti.presentation.screen.navigation + +import kotlinx.serialization.Serializable + +// todo : 리팩토링 예정 +sealed interface ShowRoute { + @Serializable + data object Detail +} \ No newline at end of file diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt index 9af738f30..84136bb97 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt @@ -12,10 +12,8 @@ import com.nexters.boolti.presentation.screen.showId fun NavGraphBuilder.paymentCompleteScreen( navController: NavHostController, - navigateTo: (String) -> Unit, navigateByDeepLink: (Uri) -> Unit, popBackStack: () -> Unit, - popInclusiveBackStack: (String) -> Unit, navigateToHome: () -> Unit, ) { composable( @@ -27,8 +25,8 @@ fun NavGraphBuilder.paymentCompleteScreen( onClickHome = navigateToHome, onClickClose = { showId?.let { showId -> - popInclusiveBackStack("${MainDestination.ShowDetail.route}/$showId") - navigateTo("${MainDestination.ShowDetail.route}/$showId") + navController.popBackStack(inclusive = true) + navController.navigate(MainRoute.ShowDetail(showId = showId)) } ?: popBackStack() }, navigateToReservation = { reservation -> diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt index a14dcc4f8..6351f40e7 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt @@ -7,6 +7,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.ShowRoute fun NavGraphBuilder.showDetailScreen( navController: NavHostController, @@ -16,9 +17,7 @@ fun NavGraphBuilder.showDetailScreen( getSharedViewModel: @Composable (NavBackStackEntry) -> ShowDetailViewModel, modifier: Modifier = Modifier, ) { - composable( - route = "detail", - ) { entry -> + composable { entry -> val showViewModel: ShowDetailViewModel = getSharedViewModel(entry) ShowDetailScreen( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt index c688129ab..1f757e663 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt @@ -98,6 +98,7 @@ import com.nexters.boolti.presentation.component.ToastSnackbarHost import com.nexters.boolti.presentation.extension.toDp import com.nexters.boolti.presentation.extension.toPx import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.qr.QrCoverView import com.nexters.boolti.presentation.theme.BooltiTheme import com.nexters.boolti.presentation.theme.Grey10 @@ -132,7 +133,7 @@ fun NavGraphBuilder.ticketDetailScreen( onClickQr = { navigateTo(MainDestination.Qr.route) }, - navigateToShowDetail = { navigateTo("${MainDestination.ShowDetail.route}/$it") }, + navigateToShowDetail = { navController.navigate(MainRoute.ShowDetail(showId = it)) }, viewModel = getSharedViewModel(entry), ) } @@ -249,7 +250,11 @@ private fun TicketDetailScreen( // 배경 블러된 이미지 Box(contentAlignment = Alignment.BottomCenter) { AsyncImage( - model = asyncImageBlurModel(context, ticketGroup.poster, radius = 24), + model = asyncImageBlurModel( + context, + ticketGroup.poster, + radius = 24 + ), modifier = Modifier .width(contentWidth.toDp()) .aspectRatio(317 / 570f) @@ -262,7 +267,12 @@ private fun TicketDetailScreen( .fillMaxWidth() .aspectRatio(317 / 125f) .background( - brush = Brush.verticalGradient(listOf(Black.copy(alpha = 0f), Black)), + brush = Brush.verticalGradient( + listOf( + Black.copy(alpha = 0f), + Black + ) + ), ) ) } @@ -308,7 +318,8 @@ private fun TicketDetailScreen( ) { Notice(notice = ticketGroup.ticketNotice) - val copiedMessage = stringResource(id = R.string.ticketing_address_copied_message) + val copiedMessage = + stringResource(id = R.string.ticketing_address_copied_message) Inquiry( hostName = ticketGroup.hostName, hostPhoneNumber = ticketGroup.hostPhoneNumber, @@ -367,7 +378,9 @@ private fun TicketDetailScreen( } if (showEnterCodeDialog) { - if (LocalDate.now().toEpochDay() < uiState.ticketGroup.showDate.toLocalDate().toEpochDay()) { + if (LocalDate.now().toEpochDay() < uiState.ticketGroup.showDate.toLocalDate() + .toEpochDay() + ) { // 아직 공연일 아님 BTDialog( showCloseButton = false, From b506e806d731cc77c09aa08e5dbd981a101de971 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 22:02:37 +0900 Subject: [PATCH 18/90] =?UTF-8?q?refactor=20:=20Ticketing=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20route=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/MainDestination.kt | 8 -------- .../screen/showdetail/ShowDetailNavigation.kt | 10 +++++++++- .../screen/ticketing/TicketingNavigation.kt | 6 ++---- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index e0ca7e245..1a608a8aa 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -5,14 +5,6 @@ import androidx.navigation.navArgument import com.nexters.boolti.domain.model.Link sealed class MainDestination(val route: String) { - data object Ticketing : MainDestination(route = "ticketing") { - val arguments = listOf( - navArgument(showId) { type = NavType.StringType }, - navArgument(salesTicketId) { type = NavType.StringType }, - navArgument(ticketCount) { type = NavType.IntType }, - navArgument(isInviteTicket) { type = NavType.BoolType }, - ) - } data object Gift : MainDestination(route = "gift/{$showId}?salesTicketId={$salesTicketId}&ticketCount={$ticketCount}") { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt index 6351f40e7..348270738 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt @@ -7,6 +7,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.navigation.ShowRoute fun NavGraphBuilder.showDetailScreen( @@ -26,7 +27,14 @@ fun NavGraphBuilder.showDetailScreen( onClickHome = navigateToHome, onClickContent = { navigateTo("content") }, onTicketSelected = { showId, ticketId, ticketCount, isInviteTicket -> - navigateTo("ticketing/$showId?salesTicketId=$ticketId&ticketCount=$ticketCount&inviteTicket=$isInviteTicket") + navController.navigate( + MainRoute.Ticketing( + showId = showId, + salesTicketId = ticketId, + ticketCount = ticketCount, + isInviteTicket = isInviteTicket, + ) + ) }, onGiftTicketSelected = { showId, ticketId, ticketCount -> navigateTo( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt index a38217736..0c1351f70 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt @@ -6,6 +6,7 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.isInviteTicket +import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.salesTicketId import com.nexters.boolti.presentation.screen.showId import com.nexters.boolti.presentation.screen.ticketCount @@ -16,10 +17,7 @@ fun NavGraphBuilder.ticketingScreen( popBackStack: () -> Unit, modifier: Modifier = Modifier, ) { - composable( - route = "${MainDestination.Ticketing.route}/{$showId}?salesTicketId={$salesTicketId}&ticketCount={$ticketCount}&inviteTicket={$isInviteTicket}", - arguments = MainDestination.Ticketing.arguments, - ) { + composable { TicketingScreen( modifier = modifier, onBackClicked = popBackStack, From b4ba3d35b6f35b00ffe1d9cf813e3599be18b289 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 22:06:13 +0900 Subject: [PATCH 19/90] =?UTF-8?q?refactor=20:=20Gift=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=20route=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nexters/boolti/presentation/screen/Main.kt | 2 +- .../boolti/presentation/screen/MainDestination.kt | 15 --------------- .../presentation/screen/gift/GiftNavigation.kt | 6 ++---- .../screen/showdetail/ShowDetailNavigation.kt | 6 +++--- 4 files changed, 6 insertions(+), 23 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 9f63712d3..e551ce5aa 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -208,7 +208,7 @@ fun MainNavigation( giftCompleteScreen( navController = navController, navigateToHome = navController::navigateToHome, - popBackStack = { navController.popBackStack(MainDestination.Gift.route, true) } + popBackStack = { navController.popBackStack(MainRoute.Gift, true) } ) businessScreen( navController = navController, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index 1a608a8aa..8847e7002 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -6,21 +6,6 @@ import com.nexters.boolti.domain.model.Link sealed class MainDestination(val route: String) { - data object Gift : - MainDestination(route = "gift/{$showId}?salesTicketId={$salesTicketId}&ticketCount={$ticketCount}") { - val arguments = listOf( - navArgument(showId) { type = NavType.StringType }, - navArgument(salesTicketId) { type = NavType.StringType }, - navArgument(ticketCount) { type = NavType.IntType }, - ) - - fun createRoute( - showId: String, - salesTicketId: String, - ticketCount: Int, - ): String = "gift/$showId?salesTicketId=$salesTicketId&ticketCount=$ticketCount" - } - data object PaymentComplete : MainDestination(route = "paymentComplete") { val arguments = listOf( navArgument(reservationId) { type = NavType.StringType }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt index 66f1771b5..da35cadfb 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt @@ -5,6 +5,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.giftScreen( navController: NavHostController, @@ -12,10 +13,7 @@ fun NavGraphBuilder.giftScreen( popBackStack: () -> Unit, modifier: Modifier = Modifier, ) { - composable( - route = MainDestination.Gift.route, - arguments = MainDestination.Gift.arguments, - ) { + composable { GiftScreen( modifier = modifier, popBackStack = popBackStack, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt index 348270738..405e4b787 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt @@ -37,11 +37,11 @@ fun NavGraphBuilder.showDetailScreen( ) }, onGiftTicketSelected = { showId, ticketId, ticketCount -> - navigateTo( - MainDestination.Gift.createRoute( + navController.navigate( + MainRoute.Gift( showId = showId, salesTicketId = ticketId, - ticketCount = ticketCount + ticketCount = ticketCount, ) ) }, From 2abe195b818303857d8ae0cdf81b4e64c01ce3be Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 22:11:27 +0900 Subject: [PATCH 20/90] =?UTF-8?q?refactor=20:=20Business=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20route=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nexters/boolti/presentation/screen/MainDestination.kt | 1 - .../presentation/screen/business/BusinessNavigation.kt | 6 ++---- .../boolti/presentation/screen/gift/GiftNavigation.kt | 2 +- .../boolti/presentation/screen/home/HomeNavigation.kt | 2 +- .../presentation/screen/ticketing/TicketingNavigation.kt | 6 +----- 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index 8847e7002..6dce065b5 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -31,7 +31,6 @@ sealed class MainDestination(val route: String) { data object HostedShows : MainDestination(route = "hostedShows") - data object Business : MainDestination(route = "business") data object AccountSetting : MainDestination(route = "accountSetting") data object Profile : MainDestination(route = "profile?userCode={$userCode}") { val arguments = listOf( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt index bd0f423bb..64df97b90 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt @@ -3,15 +3,13 @@ package com.nexters.boolti.presentation.screen.business import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable -import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.businessScreen( navController: NavHostController, popBackStack: () -> Unit, ) { - composable( - route = MainDestination.Business.route, - ) { + composable { BusinessScreen( onBackPressed = popBackStack ) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt index da35cadfb..bc1a044ba 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt @@ -17,7 +17,7 @@ fun NavGraphBuilder.giftScreen( GiftScreen( modifier = modifier, popBackStack = popBackStack, - navigateToBusiness = { navigateTo(MainDestination.Business.route) }, + navigateToBusiness = { navController.navigate(MainRoute.Business) }, navigateToComplete = { giftId -> navigateTo(MainDestination.GiftComplete.createRoute(giftId)) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index 818069a5b..0dd9792b5 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -21,7 +21,7 @@ fun NavGraphBuilder.homeScreen( navigateToAccountSetting = { navigateTo(MainDestination.AccountSetting.route) }, navigateToReservations = { navController.navigate(MainRoute.Reservations) }, navigateToProfile = { navigateTo(MainDestination.Profile.createRoute()) }, - navigateToBusiness = { navigateTo(MainDestination.Business.route) }, + navigateToBusiness = { navController.navigate(MainRoute.Business) }, navigateToLogin = { navController.navigate(MainRoute.Login) }, ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt index 0c1351f70..088b7f5af 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt @@ -5,11 +5,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination -import com.nexters.boolti.presentation.screen.isInviteTicket import com.nexters.boolti.presentation.screen.navigation.MainRoute -import com.nexters.boolti.presentation.screen.salesTicketId -import com.nexters.boolti.presentation.screen.showId -import com.nexters.boolti.presentation.screen.ticketCount fun NavGraphBuilder.ticketingScreen( navController: NavHostController, @@ -24,7 +20,7 @@ fun NavGraphBuilder.ticketingScreen( onReserved = { reservationId, showId -> navigateTo("${MainDestination.PaymentComplete.route}/$reservationId?showId=$showId") }, - navigateToBusiness = { navigateTo(MainDestination.Business.route) }, + navigateToBusiness = { navController.navigate(MainRoute.Business) }, ) } } From a7146ffc745de0a828d8cd24652be04346b566af Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 22:20:05 +0900 Subject: [PATCH 21/90] =?UTF-8?q?refactor=20:=20settings=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20route=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nexters/boolti/presentation/screen/MainDestination.kt | 1 - .../screen/accountsetting/AccountSettingNavigation.kt | 5 +---- .../boolti/presentation/screen/home/HomeNavigation.kt | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index 6dce065b5..c29c22959 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -31,7 +31,6 @@ sealed class MainDestination(val route: String) { data object HostedShows : MainDestination(route = "hostedShows") - data object AccountSetting : MainDestination(route = "accountSetting") data object Profile : MainDestination(route = "profile?userCode={$userCode}") { val arguments = listOf( navArgument(userCode) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt index 433cef7ee..02544c6fc 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt @@ -3,16 +3,13 @@ package com.nexters.boolti.presentation.screen.accountsetting import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable -import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.accountSettingScreen( navController: NavHostController, popBackStack: () -> Unit, ) { - composable( - route = MainDestination.AccountSetting.route, - ) { + composable { AccountSettingScreen( navigateBack = popBackStack, onClickResign = { navController.navigate(MainRoute.SignOut) }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index 0dd9792b5..b312fca19 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -18,7 +18,7 @@ fun NavGraphBuilder.homeScreen( navigateToShowDetail = { navController.navigate(MainRoute.ShowDetail(showId = it)) }, navigateToTicketDetail = { navigateTo("${MainDestination.TicketDetail.route}/$it") }, navigateToQrScan = { navigateTo(MainDestination.HostedShows.route) }, - navigateToAccountSetting = { navigateTo(MainDestination.AccountSetting.route) }, + navigateToAccountSetting = { navController.navigate(MainRoute.AccountSetting) }, navigateToReservations = { navController.navigate(MainRoute.Reservations) }, navigateToProfile = { navigateTo(MainDestination.Profile.createRoute()) }, navigateToBusiness = { navController.navigate(MainRoute.Business) }, From 24f3adf397be5e3d9e4bfbca987d9dea060e23f4 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 22:31:59 +0900 Subject: [PATCH 22/90] =?UTF-8?q?refactor=20:=20Profile=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20route=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/MainDestination.kt | 15 --------------- .../presentation/screen/home/HomeNavigation.kt | 2 +- .../presentation/screen/navigation/MainRoute.kt | 2 +- .../screen/profile/ProfileNavigation.kt | 6 ++---- .../screen/showdetail/ShowDetailNavigation.kt | 3 +-- 5 files changed, 5 insertions(+), 23 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index c29c22959..98dd3dbd1 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -31,20 +31,6 @@ sealed class MainDestination(val route: String) { data object HostedShows : MainDestination(route = "hostedShows") - data object Profile : MainDestination(route = "profile?userCode={$userCode}") { - val arguments = listOf( - navArgument(userCode) { - type = NavType.StringType - nullable = true - }, - ) - - fun createRoute(userCode: String? = null): String = - StringBuilder("profile").apply { - userCode?.let { append("?userCode=$it") } - }.toString() - } - data object ProfileEdit : MainDestination(route = "profileEdit") data object ProfileLinkEdit : MainDestination(route = "profileLinkEdit?id={$linkId}&title={$linkTitle}&url={$url}") { @@ -77,7 +63,6 @@ const val ticketId = "ticketId" const val ticketName = "ticketName" const val data = "data" const val reservationId = "reservationId" -const val salesTicketId = "salesTicketId" const val ticketCount = "ticketCount" const val isInviteTicket = "isInviteTicket" const val userCode = "userCode" diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index b312fca19..699b85c24 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -20,7 +20,7 @@ fun NavGraphBuilder.homeScreen( navigateToQrScan = { navigateTo(MainDestination.HostedShows.route) }, navigateToAccountSetting = { navController.navigate(MainRoute.AccountSetting) }, navigateToReservations = { navController.navigate(MainRoute.Reservations) }, - navigateToProfile = { navigateTo(MainDestination.Profile.createRoute()) }, + navigateToProfile = { navController.navigate(MainRoute.Profile()) }, navigateToBusiness = { navController.navigate(MainRoute.Business) }, navigateToLogin = { navController.navigate(MainRoute.Login) }, ) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt index 710e1e35e..d7e18f18b 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt @@ -77,7 +77,7 @@ sealed interface MainRoute { @Serializable data class Profile( - val userCode: String? + val userCode: String? = null ) : MainRoute @Serializable diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt index 00ca990a7..427dace5b 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt @@ -5,6 +5,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.profileScreen( navController: NavHostController, @@ -12,10 +13,7 @@ fun NavGraphBuilder.profileScreen( popBackStack: () -> Unit, modifier: Modifier = Modifier, ) { - composable( - route = MainDestination.Profile.route, - arguments = MainDestination.Profile.arguments, - ) { + composable { ProfileScreen( modifier = modifier, onClickBack = popBackStack, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt index 405e4b787..a81045214 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt @@ -6,7 +6,6 @@ import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable -import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.navigation.ShowRoute @@ -53,7 +52,7 @@ fun NavGraphBuilder.showDetailScreen( navigateTo("report/$showId") }, navigateToProfile = { userCode -> - navigateTo(MainDestination.Profile.createRoute(userCode)) + navController.navigate(MainRoute.Profile(userCode = userCode)) }, ) } From 66b9e83b89a385c0986617ef2b3a492e9d87fee1 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 22:37:06 +0900 Subject: [PATCH 23/90] =?UTF-8?q?refactor=20:=20GiftComplete=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20route=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/nexters/boolti/presentation/screen/Main.kt | 1 - .../boolti/presentation/screen/gift/GiftNavigation.kt | 4 +--- .../screen/giftcomplete/GiftCompleteNavigation.kt | 5 +---- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index e551ce5aa..574458ae3 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -189,7 +189,6 @@ fun MainNavigation( giftScreen( navController = navController, - navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, ) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt index bc1a044ba..c4b84deb7 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt @@ -4,12 +4,10 @@ import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable -import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.giftScreen( navController: NavHostController, - navigateTo: (String) -> Unit, popBackStack: () -> Unit, modifier: Modifier = Modifier, ) { @@ -19,7 +17,7 @@ fun NavGraphBuilder.giftScreen( popBackStack = popBackStack, navigateToBusiness = { navController.navigate(MainRoute.Business) }, navigateToComplete = { giftId -> - navigateTo(MainDestination.GiftComplete.createRoute(giftId)) + navController.navigate(MainRoute.GiftComplete(giftId = giftId)) } ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt index 5aa23d895..0a1d6e37f 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt @@ -3,7 +3,6 @@ package com.nexters.boolti.presentation.screen.giftcomplete import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable -import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.giftCompleteScreen( @@ -11,9 +10,7 @@ fun NavGraphBuilder.giftCompleteScreen( navigateToHome: () -> Unit, popBackStack: () -> Unit, ) { - composable( - route = MainDestination.GiftComplete.route, - ) { + composable { GiftCompleteScreen( onClickClose = popBackStack, onClickHome = navigateToHome, From f19396760e4070a39d18d2759320ce5d5e390090 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 22:40:05 +0900 Subject: [PATCH 24/90] =?UTF-8?q?refactor=20:=20HostedShow=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20route=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/MainDestination.kt | 10 ---------- .../presentation/screen/qr/HostedShowNavigation.kt | 6 ++---- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index 98dd3dbd1..9c6d6517a 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -13,16 +13,6 @@ sealed class MainDestination(val route: String) { ) } - data object GiftComplete : MainDestination(route = "giftComplete?giftId={giftId}") { - val arguments = listOf( - navArgument("giftId") { type = NavType.StringType }, - ) - - fun createRoute( - giftId: String, - ): String = "giftComplete?giftId=${giftId}" - } - data object TicketDetail : MainDestination(route = "tickets") { val arguments = listOf(navArgument(ticketId) { type = NavType.StringType }) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt index 5e529e205..70fa70d68 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt @@ -4,7 +4,7 @@ import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable -import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.hostedShowScreen( navController: NavHostController, @@ -12,9 +12,7 @@ fun NavGraphBuilder.hostedShowScreen( onClickShow: (showId: String, showName: String) -> Unit, modifier: Modifier = Modifier, ) { - composable( - route = MainDestination.HostedShows.route - ) { + composable { HostedShowScreen( modifier = modifier, onClickShow = onClickShow, From 5667e9e10f3c77502278ee702dacb8ed747f44ab Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 22:48:26 +0900 Subject: [PATCH 25/90] =?UTF-8?q?refactor=20:=20PaymentComplete=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20route=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/Main.kt | 2 -- .../presentation/screen/MainDestination.kt | 7 ------- .../screen/payment/PaymentNavigation.kt | 20 +++++++------------ .../screen/ticketing/TicketingNavigation.kt | 9 ++++++--- 4 files changed, 13 insertions(+), 25 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 574458ae3..86ac9fe78 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -159,7 +159,6 @@ fun MainNavigation( ticketingScreen( navController = navController, - navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, ) @@ -201,7 +200,6 @@ fun MainNavigation( paymentCompleteScreen( navController = navController, navigateByDeepLink = navController::navigate, - popBackStack = navController::popBackStack, navigateToHome = navController::navigateToHome, ) giftCompleteScreen( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index 9c6d6517a..861ea1bf9 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -6,13 +6,6 @@ import com.nexters.boolti.domain.model.Link sealed class MainDestination(val route: String) { - data object PaymentComplete : MainDestination(route = "paymentComplete") { - val arguments = listOf( - navArgument(reservationId) { type = NavType.StringType }, - navArgument(showId) { type = NavType.StringType } - ) - } - data object TicketDetail : MainDestination(route = "tickets") { val arguments = listOf(navArgument(ticketId) { type = NavType.StringType }) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt index 84136bb97..a03ab1f00 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt @@ -5,29 +5,23 @@ import androidx.core.net.toUri import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable -import com.nexters.boolti.presentation.screen.MainDestination +import androidx.navigation.toRoute import com.nexters.boolti.presentation.screen.navigation.MainRoute -import com.nexters.boolti.presentation.screen.reservationId -import com.nexters.boolti.presentation.screen.showId fun NavGraphBuilder.paymentCompleteScreen( navController: NavHostController, navigateByDeepLink: (Uri) -> Unit, - popBackStack: () -> Unit, navigateToHome: () -> Unit, ) { - composable( - route = "${MainDestination.PaymentComplete.route}/{$reservationId}?showId={$showId}", - arguments = MainDestination.PaymentComplete.arguments, - ) { - val showId = it.arguments?.getString(showId) + composable { entry -> + val route = entry.toRoute() + val showId = route.showId + PaymentCompleteScreen( onClickHome = navigateToHome, onClickClose = { - showId?.let { showId -> - navController.popBackStack(inclusive = true) - navController.navigate(MainRoute.ShowDetail(showId = showId)) - } ?: popBackStack() + navController.popBackStack(inclusive = true) + navController.navigate(MainRoute.ShowDetail(showId = showId)) }, navigateToReservation = { reservation -> navController.navigate( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt index 088b7f5af..24bf69944 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt @@ -4,12 +4,10 @@ import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable -import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.ticketingScreen( navController: NavHostController, - navigateTo: (String) -> Unit, popBackStack: () -> Unit, modifier: Modifier = Modifier, ) { @@ -18,7 +16,12 @@ fun NavGraphBuilder.ticketingScreen( modifier = modifier, onBackClicked = popBackStack, onReserved = { reservationId, showId -> - navigateTo("${MainDestination.PaymentComplete.route}/$reservationId?showId=$showId") + navController.navigate( + MainRoute.PaymentComplete( + reservationId = reservationId, + showId = showId, + ) + ) }, navigateToBusiness = { navController.navigate(MainRoute.Business) }, ) From 66053a0926606796a3f917bc3d29907cb25a8299 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 22:53:25 +0900 Subject: [PATCH 26/90] =?UTF-8?q?fix=20:=20Hosted=20Show=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EB=88=84=EB=9D=BD=EB=90=9C=20=EA=B2=83=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/nexters/boolti/presentation/screen/MainDestination.kt | 2 -- .../nexters/boolti/presentation/screen/home/HomeNavigation.kt | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index 861ea1bf9..dcdff52f3 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -12,8 +12,6 @@ sealed class MainDestination(val route: String) { data object Qr : MainDestination(route = "qr") - data object HostedShows : MainDestination(route = "hostedShows") - data object ProfileEdit : MainDestination(route = "profileEdit") data object ProfileLinkEdit : MainDestination(route = "profileLinkEdit?id={$linkId}&title={$linkTitle}&url={$url}") { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index 699b85c24..ba62a779d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -17,7 +17,7 @@ fun NavGraphBuilder.homeScreen( modifier = modifier, navigateToShowDetail = { navController.navigate(MainRoute.ShowDetail(showId = it)) }, navigateToTicketDetail = { navigateTo("${MainDestination.TicketDetail.route}/$it") }, - navigateToQrScan = { navigateTo(MainDestination.HostedShows.route) }, + navigateToQrScan = { navController.navigate(MainRoute.HostedShows) }, navigateToAccountSetting = { navController.navigate(MainRoute.AccountSetting) }, navigateToReservations = { navController.navigate(MainRoute.Reservations) }, navigateToProfile = { navController.navigate(MainRoute.Profile()) }, From 1abd04a1d38f3cf8c97d90fdc3e71cc33c59e4f3 Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 22:55:35 +0900 Subject: [PATCH 27/90] =?UTF-8?q?refactor=20:=20=EC=95=88=20=EC=93=B0?= =?UTF-8?q?=EB=8A=94=20=EB=B3=80=EC=88=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reservationdetail/ReservationDetailNavigation.kt | 2 -- .../com/nexters/boolti/presentation/screen/MainDestination.kt | 4 ---- 2 files changed, 6 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt index c7de8f073..927873e1c 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt @@ -3,9 +3,7 @@ package com.nexters.boolti.presentation.reservationdetail import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable -import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute -import com.nexters.boolti.presentation.screen.reservationId fun NavGraphBuilder.reservationDetailScreen( navController: NavHostController, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index dcdff52f3..aa5c64f56 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -42,10 +42,6 @@ sealed class MainDestination(val route: String) { const val showId = "showId" const val ticketId = "ticketId" const val ticketName = "ticketName" -const val data = "data" -const val reservationId = "reservationId" -const val ticketCount = "ticketCount" -const val isInviteTicket = "isInviteTicket" const val userCode = "userCode" const val linkId = "linkId" const val linkTitle = "linkTitle" From dabc1890eba3b077ad49a7097186538c960c461e Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 23:35:05 +0900 Subject: [PATCH 28/90] =?UTF-8?q?refactor=20:=20navController=EB=A5=BC=20?= =?UTF-8?q?=EC=A7=81=EC=A0=91=20=EB=84=98=EA=B2=A8=20=ED=8C=8C=EB=9D=BC?= =?UTF-8?q?=EB=AF=B8=ED=84=B0=20=EA=B0=84=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReservationDetailNavigation.kt | 3 +- .../boolti/presentation/screen/Main.kt | 70 ++++--------------- .../AccountSettingNavigation.kt | 3 +- .../screen/business/BusinessNavigation.kt | 3 +- .../screen/gift/GiftNavigation.kt | 3 +- .../giftcomplete/GiftCompleteNavigation.kt | 7 +- .../screen/home/HomeNavigation.kt | 3 +- .../screen/login/LoginNavigation.kt | 4 +- .../screen/payment/PaymentNavigation.kt | 8 +-- .../screen/profile/ProfileNavigation.kt | 6 +- .../screen/qr/HostedShowNavigation.kt | 3 +- .../screen/refund/RefundNavigation.kt | 3 +- .../reservations/ReservationsNavigation.kt | 3 +- .../screen/signout/SignoutNavigation.kt | 7 +- .../screen/ticketing/TicketingNavigation.kt | 3 +- 15 files changed, 34 insertions(+), 95 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt index 927873e1c..9ae16b218 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt @@ -7,11 +7,10 @@ import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.reservationDetailScreen( navController: NavHostController, - popBackStack: () -> Unit, ) { composable { ReservationDetailScreen( - onBackPressed = popBackStack, + onBackPressed = navController::popBackStack, navigateToRefund = { id, isGift -> navController.navigate(MainRoute.Refund(reservationId = id, isGift = isGift)) }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 86ac9fe78..d82cc05f0 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -98,31 +98,12 @@ fun MainNavigation( navController = navController, startDestination = MainRoute.Home, ) { - homeScreen( - navController = navController, - navigateTo = navController::navigateTo - ) - loginScreen( - navController = navController, - popBackStack = navController::popBackStack - ) - signoutScreen( - navController = navController, - navigateToHome = navController::navigateToHome, - popBackStack = navController::popBackStack - ) - reservationsScreen( - navController = navController, - popBackStack = navController::popBackStack - ) - reservationDetailScreen( - navController = navController, - popBackStack = navController::popBackStack - ) - refundScreen( - navController = navController, - popBackStack = navController::popBackStack - ) + homeScreen(navController = navController) + loginScreen(navController = navController) + signoutScreen(navController = navController) + reservationsScreen(navController = navController) + reservationDetailScreen(navController = navController) + refundScreen(navController = navController) navigation( startDestination = ShowRoute.Detail, @@ -157,10 +138,7 @@ fun MainNavigation( ) } - ticketingScreen( - navController = navController, - popBackStack = navController::popBackStack, - ) + ticketingScreen(navController = navController) navigation( route = "${MainDestination.TicketDetail.route}/{$ticketId}", @@ -186,40 +164,18 @@ fun MainNavigation( ) } - giftScreen( - navController = navController, - popBackStack = navController::popBackStack, - ) + giftScreen(navController = navController) hostedShowScreen( navController = navController, onClickShow = onClickQrScan, - popBackStack = navController::popBackStack, ) - paymentCompleteScreen( - navController = navController, - navigateByDeepLink = navController::navigate, - navigateToHome = navController::navigateToHome, - ) - giftCompleteScreen( - navController = navController, - navigateToHome = navController::navigateToHome, - popBackStack = { navController.popBackStack(MainRoute.Gift, true) } - ) - businessScreen( - navController = navController, - popBackStack = navController::popBackStack - ) - accountSettingScreen( - navController = navController, - popBackStack = navController::popBackStack, - ) - profileScreen( - navController = navController, - navigateTo = navController::navigateTo, - popBackStack = navController::popBackStack, - ) + paymentCompleteScreen(navController = navController) + giftCompleteScreen(navController = navController) + businessScreen(navController = navController) + accountSettingScreen(navController = navController) + profileScreen(navController = navController) navigation( route = "profileEditNavigation", startDestination = MainDestination.ProfileEdit.route, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt index 02544c6fc..5a3788711 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt @@ -7,11 +7,10 @@ import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.accountSettingScreen( navController: NavHostController, - popBackStack: () -> Unit, ) { composable { AccountSettingScreen( - navigateBack = popBackStack, + navigateBack = navController::popBackStack, onClickResign = { navController.navigate(MainRoute.SignOut) }, ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt index 64df97b90..aee2f8b9d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt @@ -7,11 +7,10 @@ import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.businessScreen( navController: NavHostController, - popBackStack: () -> Unit, ) { composable { BusinessScreen( - onBackPressed = popBackStack + onBackPressed = navController::popBackStack ) } } \ No newline at end of file diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt index c4b84deb7..1ad36429e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt @@ -8,13 +8,12 @@ import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.giftScreen( navController: NavHostController, - popBackStack: () -> Unit, modifier: Modifier = Modifier, ) { composable { GiftScreen( modifier = modifier, - popBackStack = popBackStack, + popBackStack = navController::popBackStack, navigateToBusiness = { navController.navigate(MainRoute.Business) }, navigateToComplete = { giftId -> navController.navigate(MainRoute.GiftComplete(giftId = giftId)) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt index 0a1d6e37f..62542d3be 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt @@ -3,17 +3,16 @@ package com.nexters.boolti.presentation.screen.giftcomplete import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.extension.navigateToHome import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.giftCompleteScreen( navController: NavHostController, - navigateToHome: () -> Unit, - popBackStack: () -> Unit, ) { composable { GiftCompleteScreen( - onClickClose = popBackStack, - onClickHome = navigateToHome, + onClickClose = { navController.popBackStack(MainRoute.Gift, true) }, + onClickHome = navController::navigateToHome, navigateToReservation = { reservation -> navController.navigate( MainRoute.ReservationDetail( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index ba62a779d..45aa3a412 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -9,14 +9,13 @@ import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.homeScreen( navController: NavHostController, - navigateTo: (String) -> Unit, modifier: Modifier = Modifier, ) { composable { HomeScreen( modifier = modifier, navigateToShowDetail = { navController.navigate(MainRoute.ShowDetail(showId = it)) }, - navigateToTicketDetail = { navigateTo("${MainDestination.TicketDetail.route}/$it") }, + navigateToTicketDetail = { navController.navigate("${MainDestination.TicketDetail.route}/$it") }, navigateToQrScan = { navController.navigate(MainRoute.HostedShows) }, navigateToAccountSetting = { navController.navigate(MainRoute.AccountSetting) }, navigateToReservations = { navController.navigate(MainRoute.Reservations) }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt index ff186bc43..40e3b3330 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt @@ -4,18 +4,16 @@ import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable -import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.loginScreen( navController: NavHostController, - popBackStack: () -> Unit, modifier: Modifier = Modifier, ) { composable { LoginScreen( modifier = modifier, - onBackPressed = popBackStack + onBackPressed = navController::popBackStack ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt index a03ab1f00..b41046017 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt @@ -1,24 +1,22 @@ package com.nexters.boolti.presentation.screen.payment -import android.net.Uri import androidx.core.net.toUri import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import androidx.navigation.toRoute +import com.nexters.boolti.presentation.extension.navigateToHome import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.paymentCompleteScreen( navController: NavHostController, - navigateByDeepLink: (Uri) -> Unit, - navigateToHome: () -> Unit, ) { composable { entry -> val route = entry.toRoute() val showId = route.showId PaymentCompleteScreen( - onClickHome = navigateToHome, + onClickHome = navController::navigateToHome, onClickClose = { navController.popBackStack(inclusive = true) navController.navigate(MainRoute.ShowDetail(showId = showId)) @@ -32,7 +30,7 @@ fun NavGraphBuilder.paymentCompleteScreen( ) }, navigateToTicketDetail = { reservation -> - navigateByDeepLink("https://app.boolti.in/tickets/${reservation.id}".toUri()) + navController.navigate("https://app.boolti.in/tickets/${reservation.id}".toUri()) }, ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt index 427dace5b..666611889 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt @@ -9,15 +9,13 @@ import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.profileScreen( navController: NavHostController, - navigateTo: (String) -> Unit, - popBackStack: () -> Unit, modifier: Modifier = Modifier, ) { composable { ProfileScreen( modifier = modifier, - onClickBack = popBackStack, - navigateToProfileEdit = { navigateTo(MainDestination.ProfileEdit.route) }, + onClickBack = navController::popBackStack, + navigateToProfileEdit = { navController.navigate(MainDestination.ProfileEdit.route) }, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt index 70fa70d68..e32959f3b 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt @@ -8,7 +8,6 @@ import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.hostedShowScreen( navController: NavHostController, - popBackStack: () -> Unit, onClickShow: (showId: String, showName: String) -> Unit, modifier: Modifier = Modifier, ) { @@ -16,7 +15,7 @@ fun NavGraphBuilder.hostedShowScreen( HostedShowScreen( modifier = modifier, onClickShow = onClickShow, - onClickBack = popBackStack + onClickBack = navController::popBackStack ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt index 3eabc1c89..609144e77 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt @@ -9,14 +9,13 @@ import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.refundScreen( navController: NavHostController, - popBackStack: () -> Unit, ) { composable { entry -> val route = entry.toRoute() RefundScreen( isGift = route.isGift, - onBackPressed = popBackStack, + onBackPressed = navController::popBackStack, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt index 3c0d8c95c..c8ddee62f 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt @@ -7,11 +7,10 @@ import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.reservationsScreen( navController: NavHostController, - popBackStack: () -> Unit, ) { composable { ReservationsScreen( - onBackPressed = popBackStack, + onBackPressed = navController::popBackStack, navigateToDetail = { id, isGift -> navController.navigate( MainRoute.ReservationDetail( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt index 054d3742b..e778aee9e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt @@ -3,18 +3,17 @@ package com.nexters.boolti.presentation.screen.signout import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.extension.navigateToHome import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.signoutScreen( navController: NavHostController, - navigateToHome: () -> Unit, - popBackStack: () -> Unit, ) { composable { SignoutScreen( - navigateToHome = navigateToHome, - navigateBack = popBackStack, + navigateToHome = navController::navigateToHome, + navigateBack = navController::popBackStack, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt index 24bf69944..65f8a2894 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt @@ -8,13 +8,12 @@ import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.ticketingScreen( navController: NavHostController, - popBackStack: () -> Unit, modifier: Modifier = Modifier, ) { composable { TicketingScreen( modifier = modifier, - onBackClicked = popBackStack, + onBackClicked = navController::popBackStack, onReserved = { reservationId, showId -> navController.navigate( MainRoute.PaymentComplete( From a3230e99918186eb727760f41a6493fe8b7ea72c Mon Sep 17 00:00:00 2001 From: algosketch Date: Thu, 24 Oct 2024 23:53:05 +0900 Subject: [PATCH 29/90] =?UTF-8?q?fix=20:=20firebase=20=EB=A1=9C=EA=B9=85?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/util/rememberNavControllerWithLog.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/rememberNavControllerWithLog.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/rememberNavControllerWithLog.kt index a01f860b0..97bb077ee 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/util/rememberNavControllerWithLog.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/rememberNavControllerWithLog.kt @@ -21,7 +21,14 @@ fun rememberNavControllerWithLog( LaunchedEffect(navController) { navController.currentBackStackEntryFlow .collect { - val screenName = it.destination.route?.substringBefore('/') ?: "" + // ex1) report/{showId} + // ex2) com.nexters.boolti.presentation.screen.navigation.MainRoute.Home + val screenName = + it.destination.route + ?.substringBefore('/') + ?.substringAfterLast(".") + ?: "" + val args = it.arguments?.keySet()?.fold(mutableMapOf()) { map, key -> if (key == "android-support-nav:controller:deepLinkIntent") return@fold map map.apply { From 2c3bcb617500e720140065a8cd180f216e9d618a Mon Sep 17 00:00:00 2001 From: algosketch Date: Fri, 25 Oct 2024 00:08:39 +0900 Subject: [PATCH 30/90] =?UTF-8?q?fix=20:=20=EB=88=84=EB=9D=BD=EB=90=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/screen/showdetail/ShowDetailNavigation.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt index a81045214..e7c242636 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt @@ -45,7 +45,7 @@ fun NavGraphBuilder.showDetailScreen( ) }, viewModel = showViewModel, - navigateToLogin = { navigateTo("login") }, + navigateToLogin = { navController.navigate(MainRoute.Login) }, navigateToImages = { index -> navigateTo("images/$index") }, navigateToReport = { val showId = entry.arguments?.getString("showId") From b7ff7c71d9993a28611c985b884481e6c230e0cd Mon Sep 17 00:00:00 2001 From: algosketch Date: Fri, 25 Oct 2024 00:13:46 +0900 Subject: [PATCH 31/90] =?UTF-8?q?fix=20:=20Login=20Screen=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/login/LoginScreen.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginScreen.kt index 776456818..3ab5cdbf2 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginScreen.kt @@ -1,10 +1,10 @@ package com.nexters.boolti.presentation.screen.login -import android.widget.Toast import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -25,7 +25,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.SpanStyle @@ -95,11 +94,14 @@ fun LoginScreen( } Scaffold( + modifier = modifier, topBar = { BtCloseableAppBar(onClickClose = onBackPressed) }, containerColor = MaterialTheme.colorScheme.background, ) { innerPadding -> Box( - modifier = modifier.padding(innerPadding), + modifier = Modifier + .padding(innerPadding) + .fillMaxSize(), contentAlignment = Alignment.Center, ) { Column( From b7da55158cce47434deb3b51549261c6c2f6e645 Mon Sep 17 00:00:00 2001 From: algosketch Date: Sat, 26 Oct 2024 19:18:10 +0900 Subject: [PATCH 32/90] =?UTF-8?q?fix=20:=20=EC=84=A0=EB=AC=BC=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20=ED=9B=84=20=EB=92=A4=EB=A1=9C=20=EA=B0=80=EA=B8=B0?= =?UTF-8?q?=20=EC=8B=9C=20=EB=B0=9C=EC=83=9D=ED=95=98=EB=8A=94=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/screen/giftcomplete/GiftCompleteNavigation.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt index 62542d3be..1196cac17 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt @@ -5,13 +5,14 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.extension.navigateToHome import com.nexters.boolti.presentation.screen.navigation.MainRoute +import com.nexters.boolti.presentation.screen.navigation.ShowRoute fun NavGraphBuilder.giftCompleteScreen( navController: NavHostController, ) { composable { GiftCompleteScreen( - onClickClose = { navController.popBackStack(MainRoute.Gift, true) }, + onClickClose = { navController.popBackStack(false) }, onClickHome = navController::navigateToHome, navigateToReservation = { reservation -> navController.navigate( From 7dc2eabeef4455e6b4a43eaf45f0a0b8e9e7e853 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Wed, 11 Dec 2024 16:15:37 +0900 Subject: [PATCH 33/90] =?UTF-8?q?Boolti-347=20feat:=20=EC=9B=B9=EB=B7=B0?= =?UTF-8?q?=20=EB=B8=8C=EB=A6=BF=EC=A7=80=20=EC=97=B0=EA=B2=B0.=20(todo:?= =?UTF-8?q?=20app=20to=20web)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/datasource/AuthTokenDataSource.kt | 23 ++++ .../boolti/data/di/DataSourceModule.kt | 11 ++ .../nexters/boolti/data/di/NetworkModule.kt | 27 +++-- .../boolti/data/network/AuthAuthenticator.kt | 23 +--- .../data/repository/AuthRepositoryImpl.kt | 15 ++- .../model/{TokenPair.kt => TokenPairs.kt} | 8 +- .../domain/repository/AuthRepository.kt | 7 +- presentation/build.gradle.kts | 3 + .../presentation/component/BtWebView.kt | 113 ++++++++++++++++++ .../ShowRegistrationScreen.kt | 37 +++++- .../ShowRegistrationViewModel.kt | 6 +- 11 files changed, 233 insertions(+), 40 deletions(-) create mode 100644 data/src/main/java/com/nexters/boolti/data/datasource/AuthTokenDataSource.kt rename domain/src/main/java/com/nexters/boolti/domain/model/{TokenPair.kt => TokenPairs.kt} (58%) diff --git a/data/src/main/java/com/nexters/boolti/data/datasource/AuthTokenDataSource.kt b/data/src/main/java/com/nexters/boolti/data/datasource/AuthTokenDataSource.kt new file mode 100644 index 000000000..172a8a366 --- /dev/null +++ b/data/src/main/java/com/nexters/boolti/data/datasource/AuthTokenDataSource.kt @@ -0,0 +1,23 @@ +package com.nexters.boolti.data.datasource + +import javax.inject.Inject + +internal class AuthTokenDataSource @Inject constructor( + private val authDataSource: AuthDataSource, + private val tokenDataSource: TokenDataSource, +) { + suspend fun getNewAccessToken(): String? { + val response = authDataSource.refresh() + val newToken = response.getOrNull() + return newToken?.let { + tokenDataSource.saveTokens( + accessToken = it.accessToken, + refreshToken = it.refreshToken, + ) + it.accessToken + } ?: run { + authDataSource.logout() + null + } + } +} diff --git a/data/src/main/java/com/nexters/boolti/data/di/DataSourceModule.kt b/data/src/main/java/com/nexters/boolti/data/di/DataSourceModule.kt index fe59d8458..ff04d2e77 100644 --- a/data/src/main/java/com/nexters/boolti/data/di/DataSourceModule.kt +++ b/data/src/main/java/com/nexters/boolti/data/di/DataSourceModule.kt @@ -3,6 +3,7 @@ package com.nexters.boolti.data.di import android.content.Context import com.google.firebase.remoteconfig.FirebaseRemoteConfig import com.nexters.boolti.data.datasource.AuthDataSource +import com.nexters.boolti.data.datasource.AuthTokenDataSource import com.nexters.boolti.data.datasource.PolicyDataSource import com.nexters.boolti.data.datasource.RemoteConfigDataSource import com.nexters.boolti.data.datasource.TokenDataSource @@ -33,6 +34,16 @@ internal object DataSourceModule { @Provides fun provideTokenDataSource(@ApplicationContext context: Context): TokenDataSource = TokenDataSource(context) + @Singleton + @Provides + fun provideAuthTokenDataSource( + authDataSource: AuthDataSource, + tokenDataSource: TokenDataSource, + ): AuthTokenDataSource = AuthTokenDataSource( + authDataSource = authDataSource, + tokenDataSource = tokenDataSource, + ) + @Singleton @Provides fun providePolicyDataSource(@ApplicationContext context: Context): PolicyDataSource = PolicyDataSource(context) diff --git a/data/src/main/java/com/nexters/boolti/data/di/NetworkModule.kt b/data/src/main/java/com/nexters/boolti/data/di/NetworkModule.kt index 16c70e6b0..b6241c0be 100644 --- a/data/src/main/java/com/nexters/boolti/data/di/NetworkModule.kt +++ b/data/src/main/java/com/nexters/boolti/data/di/NetworkModule.kt @@ -2,7 +2,7 @@ package com.nexters.boolti.data.di import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import com.nexters.boolti.data.BuildConfig -import com.nexters.boolti.data.datasource.AuthDataSource +import com.nexters.boolti.data.datasource.AuthTokenDataSource import com.nexters.boolti.data.datasource.TokenDataSource import com.nexters.boolti.data.network.AuthAuthenticator import com.nexters.boolti.data.network.AuthInterceptor @@ -114,7 +114,8 @@ internal object NetworkModule { @Singleton @Provides - fun provideTicketingService(@Named("auth") retrofit: Retrofit): TicketingService = retrofit.create() + fun provideTicketingService(@Named("auth") retrofit: Retrofit): TicketingService = + retrofit.create() @Singleton @Provides @@ -126,7 +127,8 @@ internal object NetworkModule { @Singleton @Provides - fun provideReservationService(@Named("auth") retrofit: Retrofit): ReservationService = retrofit.create() + fun provideReservationService(@Named("auth") retrofit: Retrofit): ReservationService = + retrofit.create() @Singleton @Provides @@ -134,7 +136,8 @@ internal object NetworkModule { @Singleton @Provides - fun provideAuthFileService(@Named("auth") retrofit: Retrofit): AuthFileService = retrofit.create() + fun provideAuthFileService(@Named("auth") retrofit: Retrofit): AuthFileService = + retrofit.create() @Singleton @Provides @@ -142,12 +145,16 @@ internal object NetworkModule { @Singleton @Provides - fun provideMemberService(@Named("non-auth") retrofit: Retrofit): MemberService = retrofit.create() + fun provideMemberService(@Named("non-auth") retrofit: Retrofit): MemberService = + retrofit.create() @Singleton @Provides @Named("auth") - fun provideAuthOkHttpClient(interceptor: AuthInterceptor, authenticator: AuthAuthenticator): OkHttpClient { + fun provideAuthOkHttpClient( + interceptor: AuthInterceptor, + authenticator: AuthAuthenticator + ): OkHttpClient { val loggingInterceptor = HttpLoggingInterceptor().apply { level = if (BuildConfig.DEBUG) { HttpLoggingInterceptor.Level.BODY @@ -202,12 +209,12 @@ internal object NetworkModule { @Singleton @Provides - fun provideAuthInterceptor(tokenDataSource: TokenDataSource): AuthInterceptor = AuthInterceptor(tokenDataSource) + fun provideAuthInterceptor(tokenDataSource: TokenDataSource): AuthInterceptor = + AuthInterceptor(tokenDataSource) @Singleton @Provides fun provideAuthenticator( - tokenDataSource: TokenDataSource, - authDataSource: AuthDataSource, - ): AuthAuthenticator = AuthAuthenticator(tokenDataSource, authDataSource) + authTokenDataSource: AuthTokenDataSource + ): AuthAuthenticator = AuthAuthenticator(authTokenDataSource) } diff --git a/data/src/main/java/com/nexters/boolti/data/network/AuthAuthenticator.kt b/data/src/main/java/com/nexters/boolti/data/network/AuthAuthenticator.kt index 8ae8e2327..55ac1fd5c 100644 --- a/data/src/main/java/com/nexters/boolti/data/network/AuthAuthenticator.kt +++ b/data/src/main/java/com/nexters/boolti/data/network/AuthAuthenticator.kt @@ -1,7 +1,6 @@ package com.nexters.boolti.data.network -import com.nexters.boolti.data.datasource.AuthDataSource -import com.nexters.boolti.data.datasource.TokenDataSource +import com.nexters.boolti.data.datasource.AuthTokenDataSource import kotlinx.coroutines.runBlocking import okhttp3.Authenticator import okhttp3.Request @@ -10,27 +9,11 @@ import okhttp3.Route import javax.inject.Inject internal class AuthAuthenticator @Inject constructor( - private val tokenDataSource: TokenDataSource, - private val authDataSource: AuthDataSource, + private val authTokenDataSource: AuthTokenDataSource, ) : Authenticator { override fun authenticate(route: Route?, response: Response): Request? { - val accessToken = runBlocking { getNewAccessToken() } ?: return null + val accessToken = runBlocking { authTokenDataSource.getNewAccessToken() } ?: return null return response.request.newBuilder().header("Authorization", "Bearer $accessToken").build() } - - private suspend fun getNewAccessToken(): String? { - val response = authDataSource.refresh() - val newToken = response.getOrNull() - return newToken?.let { - tokenDataSource.saveTokens( - accessToken = it.accessToken, - refreshToken = it.refreshToken, - ) - it.accessToken - } ?: run { - authDataSource.logout() - null - } - } } diff --git a/data/src/main/java/com/nexters/boolti/data/repository/AuthRepositoryImpl.kt b/data/src/main/java/com/nexters/boolti/data/repository/AuthRepositoryImpl.kt index 9835a6d9b..1ae60fd71 100644 --- a/data/src/main/java/com/nexters/boolti/data/repository/AuthRepositoryImpl.kt +++ b/data/src/main/java/com/nexters/boolti/data/repository/AuthRepositoryImpl.kt @@ -1,13 +1,15 @@ package com.nexters.boolti.data.repository import com.nexters.boolti.data.datasource.AuthDataSource +import com.nexters.boolti.data.datasource.AuthTokenDataSource import com.nexters.boolti.data.datasource.DeviceTokenDataSource import com.nexters.boolti.data.datasource.SignUpDataSource import com.nexters.boolti.data.datasource.TokenDataSource import com.nexters.boolti.data.datasource.UserDataSource import com.nexters.boolti.data.network.response.LoginResponse +import com.nexters.boolti.domain.model.AccessToken import com.nexters.boolti.domain.model.LoginUserState -import com.nexters.boolti.domain.model.TokenPair +import com.nexters.boolti.domain.model.TokenPairs import com.nexters.boolti.domain.model.User import com.nexters.boolti.domain.repository.AuthRepository import com.nexters.boolti.domain.request.EditProfileRequest @@ -23,6 +25,7 @@ import javax.inject.Inject internal class AuthRepositoryImpl @Inject constructor( private val authDataSource: AuthDataSource, private val tokenDataSource: TokenDataSource, + private val authTokenDataSource: AuthTokenDataSource, private val signUpDataSource: SignUpDataSource, private val userDateSource: UserDataSource, private val deviceTokenDataSource: DeviceTokenDataSource, @@ -72,7 +75,13 @@ internal class AuthRepositoryImpl @Inject constructor( .onSuccess { authDataSource.updateUser(it) } .mapCatching {} - override fun getTokens(): Flow = authDataSource.getTokens().map { - TokenPair(it.first, it.second) + override fun getTokens(): Flow = authDataSource.getTokens().map { + TokenPairs(it.first, it.second) + } + + override fun refreshToken(): Flow = flow { + authTokenDataSource.getNewAccessToken()?.let { + emit(AccessToken(it)) + } } } diff --git a/domain/src/main/java/com/nexters/boolti/domain/model/TokenPair.kt b/domain/src/main/java/com/nexters/boolti/domain/model/TokenPairs.kt similarity index 58% rename from domain/src/main/java/com/nexters/boolti/domain/model/TokenPair.kt rename to domain/src/main/java/com/nexters/boolti/domain/model/TokenPairs.kt index ab04154aa..e79184ff9 100644 --- a/domain/src/main/java/com/nexters/boolti/domain/model/TokenPair.kt +++ b/domain/src/main/java/com/nexters/boolti/domain/model/TokenPairs.kt @@ -1,8 +1,14 @@ package com.nexters.boolti.domain.model -data class TokenPair( +data class TokenPairs( val accessToken: String, val refreshToken: String, ) { val isLoggedIn: Boolean = accessToken.isNotBlank() && refreshToken.isNotBlank() } + +@JvmInline +value class AccessToken(val token: String) + +@JvmInline +value class RefreshToken(val token: String) diff --git a/domain/src/main/java/com/nexters/boolti/domain/repository/AuthRepository.kt b/domain/src/main/java/com/nexters/boolti/domain/repository/AuthRepository.kt index 9ac3a3359..a2b6e65f5 100644 --- a/domain/src/main/java/com/nexters/boolti/domain/repository/AuthRepository.kt +++ b/domain/src/main/java/com/nexters/boolti/domain/repository/AuthRepository.kt @@ -1,7 +1,8 @@ package com.nexters.boolti.domain.repository +import com.nexters.boolti.domain.model.AccessToken import com.nexters.boolti.domain.model.LoginUserState -import com.nexters.boolti.domain.model.TokenPair +import com.nexters.boolti.domain.model.TokenPairs import com.nexters.boolti.domain.model.User import com.nexters.boolti.domain.request.EditProfileRequest import com.nexters.boolti.domain.request.LoginRequest @@ -30,5 +31,7 @@ interface AuthRepository { suspend fun editProfile(editProfileRequest: EditProfileRequest): Result - fun getTokens(): Flow + fun getTokens(): Flow + fun refreshToken(): Flow } + diff --git a/presentation/build.gradle.kts b/presentation/build.gradle.kts index d00668f56..3598d9e7e 100644 --- a/presentation/build.gradle.kts +++ b/presentation/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.android) alias(libs.plugins.hilt) + alias(libs.plugins.kotlin.serialization) id("kotlin-kapt") id("kotlin-parcelize") } @@ -82,6 +83,8 @@ dependencies { implementation(libs.zoomable) kapt(libs.hilt.compiler) + implementation(libs.kotlinx.serialization.json) + implementation(libs.lottie) implementation(libs.bundles.coil) api(libs.kakao.login) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/component/BtWebView.kt b/presentation/src/main/java/com/nexters/boolti/presentation/component/BtWebView.kt index 41326bfa4..8a8277fa2 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/component/BtWebView.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/component/BtWebView.kt @@ -4,12 +4,22 @@ import android.annotation.SuppressLint import android.content.Context import android.net.Uri import android.util.AttributeSet +import android.webkit.JavascriptInterface import android.webkit.ValueCallback import android.webkit.WebChromeClient import android.webkit.WebView import android.webkit.WebViewClient +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObject +import timber.log.Timber +import java.util.UUID class BtWebView @JvmOverloads constructor( context: Context, @@ -21,12 +31,27 @@ class BtWebView @JvmOverloads constructor( private val _progress = MutableStateFlow(0) val progress = _progress.asStateFlow() + private val _bridgeEvent = MutableSharedFlow( + extraBufferCapacity = 5, + ) + val bridgeEvent = _bridgeEvent.asSharedFlow() + + private val json = Json { + isLenient = true + prettyPrint = true + ignoreUnknownKeys = true + } + init { isFocusable = true isFocusableInTouchMode = true setupSettings() setupWebViewClient() + setupBridge() + /*evaluate("밍슈 하이~") { + Timber.tag("MANGBAAM-BtWebView()").d("웹 콜백: $it") + }*/ } @SuppressLint("SetJavaScriptEnabled") @@ -43,6 +68,25 @@ class BtWebView @JvmOverloads constructor( webViewClient = BtWebViewClient() } + private fun setupBridge() { + addJavascriptInterface( + NativeBridge { response -> + val data = response.toBridgeData() + _bridgeEvent.tryEmit(data).also { + Timber.tag("MANGBAAM-BtWebView(setupBridge)").d("성공? $it") + } + }, + "boolti", + ) + } + + fun evaluate(data: BridgeRequest, result: (String) -> Unit = {}) { + val dataAsString = json.encodeToString(data) + evaluateJavascript("javascript:postMessage('$dataAsString')") { + result(it) + } + } + fun setWebChromeClient( launchActivity: () -> Unit, setFilePathCallback: (ValueCallback>) -> Unit, @@ -80,3 +124,72 @@ class BtWebChromeClient( return true } } + +class NativeBridge(private val callback: (BridgeResponse) -> Unit = {}) { + @JavascriptInterface + fun postMessage(jsonString: String) { + runCatching { json.decodeFromString(jsonString) } + .onSuccess(callback) + .onFailure { it.printStackTrace() } // TODO 에러 처리 + } + + companion object { + private val json = Json { + isLenient = true + prettyPrint = true + ignoreUnknownKeys = true + } + } +} + +enum class Command { + NAVIGATE_TO_SHOW_DETAIL, + NAVIGATE_BACK, + REQUEST_TOKEN, + UNKNOWN; + + companion object { + fun fromString(value: String): Command = + Command.entries.find { it.name == value.trim().uppercase() } ?: UNKNOWN + } +} + +sealed interface BridgeData { + data class ShowDetail(val id: String) : BridgeData + data object NavigateBack : BridgeData + data object RequestToken : BridgeData + data object Unknown : BridgeData +} + +@Serializable +data class BridgeResponse( + val id: String = UUID.randomUUID().toString(), + val timestamp: String = System.currentTimeMillis().toString(), + val command: String, + val data: JsonElement? = null, +) { + fun toBridgeData(): BridgeData = when (Command.fromString(command)) { + Command.NAVIGATE_TO_SHOW_DETAIL -> { + val showId = when (data) { + is JsonObject -> requireNotNull(data["showId"]?.toString()) { + Timber.tag("MANGBAAM-BridgeResponse(toBridgeData)").d("showId is undefined") + } + + else -> throw IllegalArgumentException("data is not JsonObject") + } + BridgeData.ShowDetail(showId) + } + + Command.NAVIGATE_BACK -> BridgeData.NavigateBack + Command.REQUEST_TOKEN -> BridgeData.RequestToken + Command.UNKNOWN -> BridgeData.Unknown + } +} + +@Serializable +data class BridgeRequest( + val command: String, + val data: String? = null, + val id: String = UUID.randomUUID().toString(), + val timestamp: String = System.currentTimeMillis().toString(), +) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt index aab032dff..626afaa5e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt @@ -5,7 +5,6 @@ import android.net.Uri import android.webkit.CookieManager import android.webkit.ValueCallback import android.webkit.WebStorage -import android.webkit.WebView import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts @@ -36,9 +35,12 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.nexters.boolti.presentation.BuildConfig import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.component.BTDialog +import com.nexters.boolti.presentation.component.BridgeData +import com.nexters.boolti.presentation.component.BridgeRequest import com.nexters.boolti.presentation.component.BtBackAppBar import com.nexters.boolti.presentation.component.BtCircularProgressIndicator import com.nexters.boolti.presentation.component.BtWebView +import com.nexters.boolti.presentation.component.Command import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.launch @@ -64,7 +66,7 @@ fun ShowRegistrationScreen( var showExitDialog by remember { mutableStateOf(false) } val scope = rememberCoroutineScope() - var webView: WebView? by remember { mutableStateOf(null) } + var webView: BtWebView? by remember { mutableStateOf(null) } var webviewProgress by remember { mutableIntStateOf(0) } val loading by remember { derivedStateOf { webviewProgress < 100 } } @@ -81,6 +83,14 @@ fun ShowRegistrationScreen( setCookie(url, "x-refresh-token=${tokens.refreshToken}") flush() } + /*launch { + webView?.evaluate( + BridgeRequest( + command = Command.REQUEST_TOKEN.name, + data = tokens.accessToken, + ) + ) + }*/ } } @@ -114,7 +124,28 @@ fun ShowRegistrationScreen( Timber.d("내가 만든 쿠키 : ${CookieManager.getInstance().getCookie(url)}") scope.launch { - progress.collect { webviewProgress = it } + launch { + progress.collect { webviewProgress = it } + } + launch { + bridgeEvent.collect { data -> + Timber.tag("MANGBAAM-(ShowRegistrationScreen)").d("$data") + when (data) { + is BridgeData.RequestToken -> { + evaluate( + BridgeRequest( + command = Command.REQUEST_TOKEN.name, + data = "hello", + ) + ) + } + + else -> { + Timber.tag("MANGBAAM-(ShowRegistrationScreen)").d("안 탔나?") + } + } + } + } } }.also { webView = it } }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationViewModel.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationViewModel.kt index d3999d3d7..8861822e3 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationViewModel.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationViewModel.kt @@ -13,4 +13,8 @@ class ShowRegistrationViewModel @Inject constructor( ) : ViewModel() { val tokens = authRepository.getTokens() .stateInUi(viewModelScope, null) -} \ No newline at end of file + + fun refreshToken() { + authRepository.refreshToken() + } +} From 1a9b135cf175f0e4fab5a95c5b7672436c49f6f2 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Thu, 12 Dec 2024 03:55:39 +0900 Subject: [PATCH 34/90] =?UTF-8?q?feature/Boolti-347=20feat:=20=EB=B8=8C?= =?UTF-8?q?=EB=A6=BF=EC=A7=80=20=EC=97=B0=EA=B2=B0=20=EC=84=B1=EA=B3=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/component/BtWebView.kt | 83 +++++++++++++------ .../boolti/presentation/screen/my/MyScreen.kt | 6 +- .../ShowRegistrationScreen.kt | 11 +-- 3 files changed, 61 insertions(+), 39 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/component/BtWebView.kt b/presentation/src/main/java/com/nexters/boolti/presentation/component/BtWebView.kt index 8a8277fa2..0c1b2c7e6 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/component/BtWebView.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/component/BtWebView.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.content.Context import android.net.Uri import android.util.AttributeSet +import android.webkit.ConsoleMessage import android.webkit.JavascriptInterface import android.webkit.ValueCallback import android.webkit.WebChromeClient @@ -49,9 +50,6 @@ class BtWebView @JvmOverloads constructor( setupSettings() setupWebViewClient() setupBridge() - /*evaluate("밍슈 하이~") { - Timber.tag("MANGBAAM-BtWebView()").d("웹 콜백: $it") - }*/ } @SuppressLint("SetJavaScriptEnabled") @@ -71,18 +69,26 @@ class BtWebView @JvmOverloads constructor( private fun setupBridge() { addJavascriptInterface( NativeBridge { response -> + Timber.tag("bridge").d("(WEB -> APP) $response") val data = response.toBridgeData() - _bridgeEvent.tryEmit(data).also { - Timber.tag("MANGBAAM-BtWebView(setupBridge)").d("성공? $it") - } + Timber.tag("bridge").d("(APP) 변환 후 데이터: $data") + _bridgeEvent.tryEmit(data) }, "boolti", ) } + /** + * 앱 -> 웹 데이터를 보내기 위한 함수 + * + * @param data 웹에 보낼 정보 + * @param result + */ fun evaluate(data: BridgeRequest, result: (String) -> Unit = {}) { val dataAsString = json.encodeToString(data) - evaluateJavascript("javascript:postMessage('$dataAsString')") { + Timber.tag("bridge").d("(APP -> WEB) $dataAsString") + evaluateJavascript("javascript:__boolti__webview__bridge__.postMessage($dataAsString)") { + Timber.tag("bridge").d("(APP -> WEB -> APP) 콜백: $it") result(it) } } @@ -94,7 +100,9 @@ class BtWebView @JvmOverloads constructor( webChromeClient = BtWebChromeClient( launchActivity = launchActivity, setFilePathCallback = setFilePathCallback, - onProgressChanged = { _progress.value = it }, + onProgressChanged = { + _progress.value = it + }, ) } } @@ -123,6 +131,12 @@ class BtWebChromeClient( return true } + + override fun onConsoleMessage(message: ConsoleMessage?): Boolean { + Timber.tag("webview_console Message bridge") + .d("${message?.message()} -- From line ${message?.lineNumber()} of ${message?.sourceId()}") + return true + } } class NativeBridge(private val callback: (BridgeResponse) -> Unit = {}) { @@ -142,23 +156,38 @@ class NativeBridge(private val callback: (BridgeResponse) -> Unit = {}) { } } -enum class Command { - NAVIGATE_TO_SHOW_DETAIL, - NAVIGATE_BACK, - REQUEST_TOKEN, - UNKNOWN; +sealed interface Command { + @Serializable + enum class Receive : Command { + NAVIGATE_TO_SHOW_DETAIL, + NAVIGATE_BACK, + REQUEST_TOKEN, + UNKNOWN; + + companion object { + fun fromString(value: String): Receive = + Receive.entries.find { it.name == value.trim().uppercase() } ?: UNKNOWN + } + } - companion object { - fun fromString(value: String): Command = - Command.entries.find { it.name == value.trim().uppercase() } ?: UNKNOWN + @Serializable + enum class Send : Command { + REQUEST_TOKEN, + UNKNOWN; + + companion object { + fun fromString(value: String): Send = + Send.entries.find { it.name == value.trim().uppercase() } ?: UNKNOWN + } } } sealed interface BridgeData { - data class ShowDetail(val id: String) : BridgeData - data object NavigateBack : BridgeData - data object RequestToken : BridgeData - data object Unknown : BridgeData + val uuid: String + data class ShowDetail(override val uuid: String, val id: String) : BridgeData + data class NavigateBack(override val uuid: String) : BridgeData + data class RequestToken(override val uuid: String) : BridgeData + data class Unknown(override val uuid: String) : BridgeData } @Serializable @@ -168,8 +197,8 @@ data class BridgeResponse( val command: String, val data: JsonElement? = null, ) { - fun toBridgeData(): BridgeData = when (Command.fromString(command)) { - Command.NAVIGATE_TO_SHOW_DETAIL -> { + fun toBridgeData(): BridgeData = when (Command.Receive.fromString(command)) { + Command.Receive.NAVIGATE_TO_SHOW_DETAIL -> { val showId = when (data) { is JsonObject -> requireNotNull(data["showId"]?.toString()) { Timber.tag("MANGBAAM-BridgeResponse(toBridgeData)").d("showId is undefined") @@ -177,18 +206,18 @@ data class BridgeResponse( else -> throw IllegalArgumentException("data is not JsonObject") } - BridgeData.ShowDetail(showId) + BridgeData.ShowDetail(id, showId) } - Command.NAVIGATE_BACK -> BridgeData.NavigateBack - Command.REQUEST_TOKEN -> BridgeData.RequestToken - Command.UNKNOWN -> BridgeData.Unknown + Command.Receive.NAVIGATE_BACK -> BridgeData.NavigateBack(id) + Command.Receive.REQUEST_TOKEN -> BridgeData.RequestToken(id) + Command.Receive.UNKNOWN -> BridgeData.Unknown(id) } } @Serializable data class BridgeRequest( - val command: String, + val command: Command.Send, val data: String? = null, val id: String = UUID.randomUUID().toString(), val timestamp: String = System.currentTimeMillis().toString(), diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt index c3ba4ba93..4aafe2cdb 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt @@ -1,6 +1,5 @@ package com.nexters.boolti.presentation.screen.my -import android.widget.Toast import androidx.annotation.DrawableRes import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -78,10 +77,7 @@ fun MyScreen( onClickHeaderButton = if (user != null) navigateToProfile else requireLogin, onClickAccountSetting = if (user != null) onClickAccountSetting else requireLogin, onClickReservations = if (user != null) navigateToReservations else requireLogin, - onClickRegisterShow = { - uriHandler.openUri(url) - Toast.makeText(context, "공연 등록을 위해 웹으로 이동합니다", Toast.LENGTH_LONG).show() - },// navigateToShowRegistration, // TODO 추후 인앱 공연 등록 반영 시 주석 해제 + onClickRegisterShow = navigateToShowRegistration, onClickQrScan = if (user != null) onClickQrScan else requireLogin, ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt index 626afaa5e..9ec236c1a 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt @@ -11,7 +11,6 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeOut import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding @@ -46,7 +45,6 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.launch import timber.log.Timber -@OptIn(ExperimentalLayoutApi::class) @SuppressLint("SetJavaScriptEnabled") @Composable fun ShowRegistrationScreen( @@ -129,20 +127,19 @@ fun ShowRegistrationScreen( } launch { bridgeEvent.collect { data -> - Timber.tag("MANGBAAM-(ShowRegistrationScreen)").d("$data") + Timber.tag("bridge").d("bridgeEvent 수집(ShowRegistrationScreen) - $data") when (data) { is BridgeData.RequestToken -> { evaluate( BridgeRequest( - command = Command.REQUEST_TOKEN.name, + id = data.uuid, + command = Command.Send.REQUEST_TOKEN, data = "hello", ) ) } - else -> { - Timber.tag("MANGBAAM-(ShowRegistrationScreen)").d("안 탔나?") - } + else -> Unit } } } From 74c57cd2fb4476c7d0fd975cd7a9f22e7c957623 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sun, 15 Dec 2024 19:45:51 +0900 Subject: [PATCH 35/90] =?UTF-8?q?feature/Boolti-347=20feat:=20=EB=B8=8C?= =?UTF-8?q?=EB=A6=BF=EC=A7=80=20=EC=BD=94=EB=93=9C=20=EA=B0=88=EC=95=84=20?= =?UTF-8?q?=EC=97=8E=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/component/BtWebView.kt | 167 ++++-------------- .../ShowRegistrationScreen.kt | 46 ++--- .../util/bridge/BridgeCallbackHandler.kt | 12 ++ .../presentation/util/bridge/BridgeDto.kt | 12 ++ .../presentation/util/bridge/BridgeManager.kt | 86 +++++++++ .../presentation/util/bridge/CommandType.kt | 16 ++ 6 files changed, 175 insertions(+), 164 deletions(-) create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeDto.kt create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/CommandType.kt diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/component/BtWebView.kt b/presentation/src/main/java/com/nexters/boolti/presentation/component/BtWebView.kt index 0c1b2c7e6..96df6f503 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/component/BtWebView.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/component/BtWebView.kt @@ -10,17 +10,13 @@ import android.webkit.ValueCallback import android.webkit.WebChromeClient import android.webkit.WebView import android.webkit.WebViewClient -import kotlinx.coroutines.flow.MutableSharedFlow +import com.nexters.boolti.presentation.util.bridge.BridgeDto +import com.nexters.boolti.presentation.util.bridge.BridgeManager import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString +import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonObject import timber.log.Timber -import java.util.UUID class BtWebView @JvmOverloads constructor( context: Context, @@ -32,24 +28,12 @@ class BtWebView @JvmOverloads constructor( private val _progress = MutableStateFlow(0) val progress = _progress.asStateFlow() - private val _bridgeEvent = MutableSharedFlow( - extraBufferCapacity = 5, - ) - val bridgeEvent = _bridgeEvent.asSharedFlow() - - private val json = Json { - isLenient = true - prettyPrint = true - ignoreUnknownKeys = true - } - init { isFocusable = true isFocusableInTouchMode = true setupSettings() setupWebViewClient() - setupBridge() } @SuppressLint("SetJavaScriptEnabled") @@ -66,33 +50,6 @@ class BtWebView @JvmOverloads constructor( webViewClient = BtWebViewClient() } - private fun setupBridge() { - addJavascriptInterface( - NativeBridge { response -> - Timber.tag("bridge").d("(WEB -> APP) $response") - val data = response.toBridgeData() - Timber.tag("bridge").d("(APP) 변환 후 데이터: $data") - _bridgeEvent.tryEmit(data) - }, - "boolti", - ) - } - - /** - * 앱 -> 웹 데이터를 보내기 위한 함수 - * - * @param data 웹에 보낼 정보 - * @param result - */ - fun evaluate(data: BridgeRequest, result: (String) -> Unit = {}) { - val dataAsString = json.encodeToString(data) - Timber.tag("bridge").d("(APP -> WEB) $dataAsString") - evaluateJavascript("javascript:__boolti__webview__bridge__.postMessage($dataAsString)") { - Timber.tag("bridge").d("(APP -> WEB -> APP) 콜백: $it") - result(it) - } - } - fun setWebChromeClient( launchActivity: () -> Unit, setFilePathCallback: (ValueCallback>) -> Unit, @@ -100,10 +57,40 @@ class BtWebView @JvmOverloads constructor( webChromeClient = BtWebChromeClient( launchActivity = launchActivity, setFilePathCallback = setFilePathCallback, - onProgressChanged = { - _progress.value = it + onProgressChanged = { _progress.value = it }, + ) + } + + suspend fun setBridgeManager(bridgeManager: BridgeManager) { + addJavascriptInterface( + object { + @JavascriptInterface + fun postMessage(message: String) { + Timber.tag("webview_bridge").d("(WEB -> APP) $message 수신") + try { + // JSON 메시지 파싱 + val dto = Json.decodeFromString(BridgeDto.serializer(), message) + bridgeManager.handleIncomingData(dto) + } catch (e: SerializationException) { + Timber.tag("webview_bridge") + .e(e, "(WEB -> APP) 유효하지 않은 JSON 포맷: $message") + } catch (e: IllegalArgumentException) { + Timber.tag("webview_bridge") + .e(e, "(WEB -> APP) BridgeDto 타입으로 파싱 실패: $message") + } catch (e: Exception) { + Timber.tag("webview_bridge") + .e(e, "(WEB -> APP) 알 수 없는 에러") + e.printStackTrace() // 에러 처리 + } + } }, + bridgeManager.bridgeName, ) + bridgeManager.dataToSendWeb.collect { + evaluateJavascript(it) { result -> + Timber.tag("webview_bridge").d("(APP -> WEB)\n\t$it\n전송 결과:\n\t$result") + } + } } } @@ -138,87 +125,3 @@ class BtWebChromeClient( return true } } - -class NativeBridge(private val callback: (BridgeResponse) -> Unit = {}) { - @JavascriptInterface - fun postMessage(jsonString: String) { - runCatching { json.decodeFromString(jsonString) } - .onSuccess(callback) - .onFailure { it.printStackTrace() } // TODO 에러 처리 - } - - companion object { - private val json = Json { - isLenient = true - prettyPrint = true - ignoreUnknownKeys = true - } - } -} - -sealed interface Command { - @Serializable - enum class Receive : Command { - NAVIGATE_TO_SHOW_DETAIL, - NAVIGATE_BACK, - REQUEST_TOKEN, - UNKNOWN; - - companion object { - fun fromString(value: String): Receive = - Receive.entries.find { it.name == value.trim().uppercase() } ?: UNKNOWN - } - } - - @Serializable - enum class Send : Command { - REQUEST_TOKEN, - UNKNOWN; - - companion object { - fun fromString(value: String): Send = - Send.entries.find { it.name == value.trim().uppercase() } ?: UNKNOWN - } - } -} - -sealed interface BridgeData { - val uuid: String - data class ShowDetail(override val uuid: String, val id: String) : BridgeData - data class NavigateBack(override val uuid: String) : BridgeData - data class RequestToken(override val uuid: String) : BridgeData - data class Unknown(override val uuid: String) : BridgeData -} - -@Serializable -data class BridgeResponse( - val id: String = UUID.randomUUID().toString(), - val timestamp: String = System.currentTimeMillis().toString(), - val command: String, - val data: JsonElement? = null, -) { - fun toBridgeData(): BridgeData = when (Command.Receive.fromString(command)) { - Command.Receive.NAVIGATE_TO_SHOW_DETAIL -> { - val showId = when (data) { - is JsonObject -> requireNotNull(data["showId"]?.toString()) { - Timber.tag("MANGBAAM-BridgeResponse(toBridgeData)").d("showId is undefined") - } - - else -> throw IllegalArgumentException("data is not JsonObject") - } - BridgeData.ShowDetail(id, showId) - } - - Command.Receive.NAVIGATE_BACK -> BridgeData.NavigateBack(id) - Command.Receive.REQUEST_TOKEN -> BridgeData.RequestToken(id) - Command.Receive.UNKNOWN -> BridgeData.Unknown(id) - } -} - -@Serializable -data class BridgeRequest( - val command: Command.Send, - val data: String? = null, - val id: String = UUID.randomUUID().toString(), - val timestamp: String = System.currentTimeMillis().toString(), -) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt index 9ec236c1a..f1a4bf16e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt @@ -34,12 +34,12 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.nexters.boolti.presentation.BuildConfig import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.component.BTDialog -import com.nexters.boolti.presentation.component.BridgeData -import com.nexters.boolti.presentation.component.BridgeRequest import com.nexters.boolti.presentation.component.BtBackAppBar import com.nexters.boolti.presentation.component.BtCircularProgressIndicator import com.nexters.boolti.presentation.component.BtWebView -import com.nexters.boolti.presentation.component.Command +import com.nexters.boolti.presentation.util.bridge.BridgeCallbackHandler +import com.nexters.boolti.presentation.util.bridge.BridgeManager +import com.nexters.boolti.presentation.util.bridge.TokenDto import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.launch @@ -68,6 +68,16 @@ fun ShowRegistrationScreen( var webviewProgress by remember { mutableIntStateOf(0) } val loading by remember { derivedStateOf { webviewProgress < 100 } } + LaunchedEffect(webView != null) { + webView?.setBridgeManager( + BridgeManager( + object : BridgeCallbackHandler { + override fun fetchToken(): TokenDto = TokenDto("test") + } + ) + ) + } + LaunchedEffect(Unit) { CookieManager.getInstance().removeAllCookies(null) WebStorage.getInstance().deleteAllData() @@ -81,14 +91,6 @@ fun ShowRegistrationScreen( setCookie(url, "x-refresh-token=${tokens.refreshToken}") flush() } - /*launch { - webView?.evaluate( - BridgeRequest( - command = Command.REQUEST_TOKEN.name, - data = tokens.accessToken, - ) - ) - }*/ } } @@ -122,27 +124,7 @@ fun ShowRegistrationScreen( Timber.d("내가 만든 쿠키 : ${CookieManager.getInstance().getCookie(url)}") scope.launch { - launch { - progress.collect { webviewProgress = it } - } - launch { - bridgeEvent.collect { data -> - Timber.tag("bridge").d("bridgeEvent 수집(ShowRegistrationScreen) - $data") - when (data) { - is BridgeData.RequestToken -> { - evaluate( - BridgeRequest( - id = data.uuid, - command = Command.Send.REQUEST_TOKEN, - data = "hello", - ) - ) - } - - else -> Unit - } - } - } + progress.collect { webviewProgress = it } } }.also { webView = it } }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt new file mode 100644 index 000000000..95186730e --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt @@ -0,0 +1,12 @@ +package com.nexters.boolti.presentation.util.bridge + +import kotlinx.serialization.Serializable + +interface BridgeCallbackHandler { + fun fetchToken(): TokenDto +} + +@Serializable +data class TokenDto( + val token: String, +) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeDto.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeDto.kt new file mode 100644 index 000000000..51eae6cd3 --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeDto.kt @@ -0,0 +1,12 @@ +package com.nexters.boolti.presentation.util.bridge + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement + +@Serializable +data class BridgeDto( + val id: String, + val command: CommandType, + val timestamp: Long = System.currentTimeMillis(), + val data: JsonElement? = null, +) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt new file mode 100644 index 000000000..df88226f0 --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt @@ -0,0 +1,86 @@ +package com.nexters.boolti.presentation.util.bridge + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.launch +import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.encodeToJsonElement + +class BridgeManager( + private val callbackHandler: BridgeCallbackHandler, + private val scope: CoroutineScope = CoroutineScope(SupervisorJob()), + val bridgeName: String = DEFAULT_BRIDGE_NAME, + private val webCallbackName: String = DEFAULT_WEB_CALLBACK_NAME, +) { + private val originDataMap = mutableMapOf() + private val _dataToSendToWeb = Channel() + val dataToSendWeb = _dataToSendToWeb.receiveAsFlow() + + fun handleIncomingData(data: BridgeDto) { + originDataMap[data.id] = data + + when (data.command) { + CommandType.REQUEST_TOKEN -> { + val token = callbackHandler.fetchToken() + callbackToWeb(data, json.encodeToJsonElement(token)) + } + + else -> callbackToWeb(data, null) + } + } + + fun sendDataToWeb(data: String) { + scope.launch { + _dataToSendToWeb.send("javascript:$webCallbackName($data)") + } + } + + private fun callbackToWeb( + originData: BridgeDto, + responseData: JsonElement? + ) { + val responseDto = originData.toBridgeCallbackDto( + data = responseData + ) + val json = json.encodeToString(responseDto) + sendDataToWeb(json) + } + + companion object { + private val json = Json { + isLenient = true + prettyPrint = true + ignoreUnknownKeys = true + } + + const val DEFAULT_BRIDGE_NAME = "boolti" + const val DEFAULT_WEB_CALLBACK_NAME = "__boolti__webview__bridge__.postMessage" + } +} + +@Serializable +private data class BridgeCallbackDto( + val id: String, + val command: CommandType, + val data: String, + val timestamp: Long = System.currentTimeMillis(), +) + +private fun BridgeDto.toBridgeCallbackDto(data: JsonElement? = null): BridgeCallbackDto { + val json = Json { + isLenient = true + ignoreUnknownKeys = true + } + val dataJsonElement = data ?: this.data ?: json.encodeToJsonElement(Unit) + return BridgeCallbackDto( + id = id, + command = command, + data = json.encodeToString(dataJsonElement), + timestamp = timestamp, + ) +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/CommandType.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/CommandType.kt new file mode 100644 index 000000000..ed728aeb3 --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/CommandType.kt @@ -0,0 +1,16 @@ +package com.nexters.boolti.presentation.util.bridge + +import kotlinx.serialization.Serializable + +@Serializable +enum class CommandType { + NAVIGATE_TO_SHOW_DETAIL, + NAVIGATE_BACK, + REQUEST_TOKEN, + UNKNOWN; + + companion object { + fun fromString(value: String): CommandType = + CommandType.entries.find { it.name == value.trim().uppercase() } ?: UNKNOWN + } +} From a4d86574eebd39ca86ef7b5f85ba78d9debafb8f Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sun, 15 Dec 2024 22:56:17 +0900 Subject: [PATCH 36/90] =?UTF-8?q?feature/Boolti-347=20docs:=20=EB=B8=8C?= =?UTF-8?q?=EB=A6=BF=EC=A7=80=20=EA=B4=80=EB=A0=A8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/bridge/BridgeCallbackHandler.kt | 5 +++ .../presentation/util/bridge/BridgeManager.kt | 31 +++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt index 95186730e..b13ffe815 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt @@ -2,6 +2,11 @@ package com.nexters.boolti.presentation.util.bridge import kotlinx.serialization.Serializable +/** + * 브릿지를 통해 받은 데이터로부터 처리해야 하는 액션을 정의하는 핸들러 + * + * 새로운 액션이 정의되는 경우 [BridgeManager.handleIncomingData] 도 함께 수정되어야 한다. + */ interface BridgeCallbackHandler { fun fetchToken(): TokenDto } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt index df88226f0..3069373a5 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt @@ -21,8 +21,16 @@ class BridgeManager( private val _dataToSendToWeb = Channel() val dataToSendWeb = _dataToSendToWeb.receiveAsFlow() + /** + * 웹뷰에서 호출하는 메서드 + * + * 웹 브릿지를 통해 받은 데이터의 타입에 따라 [BridgeCallbackHandler]의 메서드를 호출한다. + * 이후 함수 내부에서 [BridgeCallbackHandler]의 메서드로부터 받은 결과를 웹으로 콜백한다. + * + * @param data + */ fun handleIncomingData(data: BridgeDto) { - originDataMap[data.id] = data + originDataMap[data.id] = data // 디버깅을 위한 캐싱 when (data.command) { CommandType.REQUEST_TOKEN -> { @@ -34,12 +42,27 @@ class BridgeManager( } } + /** + * WebView 에서 웹에 evaluation 할 자바스크립트 코드 전달 + * + * @param data + */ fun sendDataToWeb(data: String) { scope.launch { _dataToSendToWeb.send("javascript:$webCallbackName($data)") } } + /** + * 웹에서 브릿지를 통해 데이터를 받은 경우, 웹으로 콜백 해줘야 한다. (콜백 해주지 않으면 웹의 비동기 코드가 종료되지 않음) + * + * 예를 들어 화면 이동의 경우 웹으로 전달할 데이터가 없으니 [BridgeDto.timestamp]를 제외한 나머지 정보는 에코 방식으로 그대로 콜백한다. + * + * 토큰 요청과 같이 웹으로 전달할 데이터가 있는 경우 [BridgeDto.data]에 웹과 맞춘 형태로 데이터를 포함시키고, 나머지 [BridgeDto.id]와 [BridgeDto.command]는 그대로 콜백한다. + * + * @param originData 웹에서 브릿지를 통해 받은 원본 데이터 + * @param responseData [BridgeDto.data]에 포함될 데이터. `null` 이라면 원본 [BridgeDto.data] 를 사용한다. + */ private fun callbackToWeb( originData: BridgeDto, responseData: JsonElement? @@ -48,6 +71,8 @@ class BridgeManager( data = responseData ) val json = json.encodeToString(responseDto) + .replace("\"{\"", "{") + .replace("\"}\"", "}") sendDataToWeb(json) } @@ -67,8 +92,8 @@ class BridgeManager( private data class BridgeCallbackDto( val id: String, val command: CommandType, - val data: String, val timestamp: Long = System.currentTimeMillis(), + val data: String?, ) private fun BridgeDto.toBridgeCallbackDto(data: JsonElement? = null): BridgeCallbackDto { @@ -76,7 +101,7 @@ private fun BridgeDto.toBridgeCallbackDto(data: JsonElement? = null): BridgeCall isLenient = true ignoreUnknownKeys = true } - val dataJsonElement = data ?: this.data ?: json.encodeToJsonElement(Unit) + val dataJsonElement = data ?: this.data return BridgeCallbackDto( id = id, command = command, From c51f7eb2208bfb9303f70e085aa651684078c8c3 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Mon, 16 Dec 2024 00:29:27 +0900 Subject: [PATCH 37/90] =?UTF-8?q?feature/Boolti-347=20feat:=20=EB=B8=8C?= =?UTF-8?q?=EB=A6=BF=EC=A7=80=20=EC=86=A1=EC=88=98=EC=8B=A0=20=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=20=ED=86=B5=EC=9D=BC...=20=EB=B8=8C=EB=A6=BF=EC=A7=80?= =?UTF-8?q?=20=EC=97=B0=EA=B2=B0=20=EC=A7=84=EC=A7=9C=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ShowRegistrationScreen.kt | 5 ++-- .../presentation/util/bridge/BridgeManager.kt | 30 ++----------------- 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt index f1a4bf16e..b44784e01 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt @@ -71,9 +71,10 @@ fun ShowRegistrationScreen( LaunchedEffect(webView != null) { webView?.setBridgeManager( BridgeManager( - object : BridgeCallbackHandler { + callbackHandler = object : BridgeCallbackHandler { override fun fetchToken(): TokenDto = TokenDto("test") - } + }, + scope = scope, ) ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt index 3069373a5..469a57ea2 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt @@ -5,7 +5,6 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch -import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement @@ -67,12 +66,11 @@ class BridgeManager( originData: BridgeDto, responseData: JsonElement? ) { - val responseDto = originData.toBridgeCallbackDto( - data = responseData + val responseDto = originData.copy( + data = responseData, + timestamp = System.currentTimeMillis(), ) val json = json.encodeToString(responseDto) - .replace("\"{\"", "{") - .replace("\"}\"", "}") sendDataToWeb(json) } @@ -87,25 +85,3 @@ class BridgeManager( const val DEFAULT_WEB_CALLBACK_NAME = "__boolti__webview__bridge__.postMessage" } } - -@Serializable -private data class BridgeCallbackDto( - val id: String, - val command: CommandType, - val timestamp: Long = System.currentTimeMillis(), - val data: String?, -) - -private fun BridgeDto.toBridgeCallbackDto(data: JsonElement? = null): BridgeCallbackDto { - val json = Json { - isLenient = true - ignoreUnknownKeys = true - } - val dataJsonElement = data ?: this.data - return BridgeCallbackDto( - id = id, - command = command, - data = json.encodeToString(dataJsonElement), - timestamp = timestamp, - ) -} From 529a21b3098156f2eb6e8aa3c5d824ab45d7b379 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sun, 22 Dec 2024 01:10:17 +0900 Subject: [PATCH 38/90] =?UTF-8?q?Boolti-343=20style:=20edge=20to=20edge=20?= =?UTF-8?q?=ED=99=9C=EC=84=B1=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/Main.kt | 6 +- .../presentation/screen/MainActivity.kt | 2 + .../presentation/screen/home/HomeScreen.kt | 2 +- .../presentation/screen/show/ShowScreen.kt | 121 +++++++++--------- .../boolti/presentation/theme/Theme.kt | 9 +- 5 files changed, 67 insertions(+), 73 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 32c7e7061..1d49edb14 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -1,5 +1,6 @@ package com.nexters.boolti.presentation.screen +import android.annotation.SuppressLint import android.content.Intent import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -58,6 +59,7 @@ val LocalSnackbarController = staticCompositionLocalOf { SnackbarController(SnackbarHostState()) } +@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @Composable fun Main(onClickQrScan: (showId: String, showName: String) -> Unit) { val modifier = Modifier.fillMaxSize() @@ -73,7 +75,7 @@ fun Main(onClickQrScan: (showId: String, showName: String) -> Unit) { hostState = snackbarHostState, ) }, - ) { innerPadding -> + ) { CompositionLocalProvider( LocalSnackbarController provides SnackbarController( snackbarHostState, @@ -81,7 +83,7 @@ fun Main(onClickQrScan: (showId: String, showName: String) -> Unit) { ) ) { MainNavigation( - modifier = modifier.padding(innerPadding), + modifier = modifier, onClickQrScan = onClickQrScan, ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainActivity.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainActivity.kt index d1ee937d8..d7bfbe391 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainActivity.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainActivity.kt @@ -7,6 +7,7 @@ import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -26,6 +27,7 @@ import timber.log.Timber class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + enableEdgeToEdge() setContent { BooltiTheme { Surface( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt index 878bf106e..07f8b7a25 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt @@ -141,7 +141,7 @@ fun HomeScreen( ) ) { ShowScreen( - modifier = modifier.padding(innerPadding), + modifier = Modifier.padding(innerPadding), onClickShowItem = onClickShowItem, navigateToBusiness = navigateToBusiness, navigateToShowRegistration = { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt index f612fc7bc..43cbb8ebc 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt @@ -27,7 +27,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextFieldDefaults -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable @@ -113,75 +112,71 @@ fun ShowScreen( } } - Scaffold( + Box( modifier = modifier.nestedScroll(nestedScrollConnection), - ) { innerPadding -> - Box( - modifier = Modifier.padding(innerPadding), - contentAlignment = Alignment.TopCenter, + contentAlignment = Alignment.TopCenter, + ) { + LazyVerticalGrid( + modifier = Modifier + .padding(horizontal = marginHorizontal), + state = lazyGridState, + columns = GridCells.Adaptive(minSize = 150.dp), + horizontalArrangement = Arrangement.spacedBy(15.dp), + verticalArrangement = Arrangement.spacedBy(28.dp), + contentPadding = PaddingValues(top = 12.dp + appbarHeight), ) { - LazyVerticalGrid( - modifier = Modifier - .padding(horizontal = marginHorizontal), - state = lazyGridState, - columns = GridCells.Adaptive(minSize = 150.dp), - horizontalArrangement = Arrangement.spacedBy(15.dp), - verticalArrangement = Arrangement.spacedBy(28.dp), - contentPadding = PaddingValues(top = 12.dp + appbarHeight), - ) { - items( - count = uiState.shows.size.coerceAtMost(4), - key = { index -> uiState.shows[index].id }) { index -> - ShowFeed( - show = uiState.shows[index], - modifier = Modifier - .clickable { onClickShowItem(uiState.shows[index].id) }, - ) - } + items( + count = uiState.shows.size.coerceAtMost(4), + key = { index -> uiState.shows[index].id }) { index -> + ShowFeed( + show = uiState.shows[index], + modifier = Modifier + .clickable { onClickShowItem(uiState.shows[index].id) }, + ) + } - // 4개의 공연 뒤 보이는 배너 - if (uiState.shows.isNotEmpty()) item( - span = { GridItemSpan(2) }, - ) { - Banner( - modifier = Modifier.fillMaxWidth(), - navigateToShowRegistration = navigateToShowRegistration, - ) - } + // 4개의 공연 뒤 보이는 배너 + if (uiState.shows.isNotEmpty()) item( + span = { GridItemSpan(2) }, + ) { + Banner( + modifier = Modifier.fillMaxWidth(), + navigateToShowRegistration = navigateToShowRegistration, + ) + } - // 나머지 공연 목록 - items( - count = (uiState.shows.size - 4).coerceAtLeast(0), - key = { index -> uiState.shows[index + 4].id }) { index -> - ShowFeed( - show = uiState.shows[index + 4], - modifier = Modifier - .clickable { onClickShowItem(uiState.shows[index + 4].id) }, - ) - } + // 나머지 공연 목록 + items( + count = (uiState.shows.size - 4).coerceAtLeast(0), + key = { index -> uiState.shows[index + 4].id }) { index -> + ShowFeed( + show = uiState.shows[index + 4], + modifier = Modifier + .clickable { onClickShowItem(uiState.shows[index + 4].id) }, + ) + } - item( - span = { GridItemSpan(2) }, - ) { - BusinessInformation( - modifier = Modifier.padding(bottom = 12.dp), - onClick = navigateToBusiness - ) - } + item( + span = { GridItemSpan(2) }, + ) { + BusinessInformation( + modifier = Modifier.padding(bottom = 12.dp), + onClick = navigateToBusiness + ) } - ShowAppBar( - modifier = Modifier.offset { - IntOffset( - x = 0, - y = appbarOffsetHeightPx.coerceAtLeast(-changeableAppBarHeightPx).toInt(), - ) - }, - nickname = nickname.ifBlank { stringResource(id = R.string.nickname_default) }, - text = uiState.keyword, - onKeywordChanged = viewModel::updateKeyword, - search = viewModel::search, - ) } + ShowAppBar( + modifier = Modifier.offset { + IntOffset( + x = 0, + y = appbarOffsetHeightPx.coerceAtLeast(-changeableAppBarHeightPx).toInt(), + ) + }, + nickname = nickname.ifBlank { stringResource(id = R.string.nickname_default) }, + text = uiState.keyword, + onKeywordChanged = viewModel::updateKeyword, + search = viewModel::search, + ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/theme/Theme.kt b/presentation/src/main/java/com/nexters/boolti/presentation/theme/Theme.kt index accf42e2e..045d8bfc9 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/theme/Theme.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/theme/Theme.kt @@ -1,14 +1,9 @@ package com.nexters.boolti.presentation.theme -import android.app.Activity import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.platform.LocalView -import androidx.core.view.WindowCompat private val DarkColorScheme = darkColorScheme( primary = Orange01, @@ -54,7 +49,7 @@ fun BooltiTheme( darkTheme -> DarkColorScheme else -> LightColorScheme } - val view = LocalView.current + /*val view = LocalView.current if (!view.isInEditMode) { SideEffect { val window = (view.context as Activity).window @@ -62,7 +57,7 @@ fun BooltiTheme( window.navigationBarColor = Grey95.toArgb() WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme } - } + }*/ MaterialTheme( colorScheme = colorScheme, From 8c75863778e85352f92deab3b28a19b939f561f0 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sun, 22 Dec 2024 01:54:48 +0900 Subject: [PATCH 39/90] =?UTF-8?q?Boolti-343=20style:=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EB=B0=94=20=ED=8C=A8=EB=94=A9=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 계정 설정 - 사업자 정보 - 주최 공연 관리 (입장 확인) - 결제 완료 화면 - QR 스캔 화면 - 결제 내역 - 티켓 상세 --- .../com/nexters/boolti/presentation/screen/MainActivity.kt | 6 +++++- .../screen/accountsetting/AccountSettingScreen.kt | 3 ++- .../boolti/presentation/screen/business/BusinessScreen.kt | 2 ++ .../presentation/screen/payment/PaymentCompleteScreen.kt | 2 ++ .../boolti/presentation/screen/qr/HostedShowScreen.kt | 2 ++ .../nexters/boolti/presentation/screen/qr/QrScanScreen.kt | 2 ++ .../presentation/screen/reservations/ReservationsScreen.kt | 2 ++ .../presentation/screen/ticket/detail/TicketDetailScreen.kt | 4 +++- 8 files changed, 20 insertions(+), 3 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainActivity.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainActivity.kt index d7bfbe391..f69fad5cd 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainActivity.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainActivity.kt @@ -3,9 +3,11 @@ package com.nexters.boolti.presentation.screen import android.Manifest import android.app.NotificationChannel import android.app.NotificationManager +import android.graphics.Color import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity +import androidx.activity.SystemBarStyle import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.fillMaxSize @@ -27,7 +29,9 @@ import timber.log.Timber class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - enableEdgeToEdge() + enableEdgeToEdge( + statusBarStyle = SystemBarStyle.dark(Color.TRANSPARENT), + ) setContent { BooltiTheme { Surface( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt index 01aed7d8a..d2324f0a2 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll @@ -72,7 +73,7 @@ fun AccountSettingScreen( } AccountSettingScreen( - modifier = modifier, + modifier = modifier.statusBarsPadding(), userCode = user?.userCode ?: "", onClickBack = navigateBack, requireLogout = viewModel::logout, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessScreen.kt index cf1a7acaf..2cde8b4ce 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessScreen.kt @@ -9,6 +9,7 @@ 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.statusBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll @@ -38,6 +39,7 @@ fun BusinessScreen( onBackPressed: () -> Unit, ) { Scaffold( + modifier = Modifier.statusBarsPadding(), contentColor = MaterialTheme.colorScheme.background, topBar = { BtBackAppBar( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentCompleteScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentCompleteScreen.kt index 7108b2589..c8444e095 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentCompleteScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentCompleteScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -59,6 +60,7 @@ fun PaymentCompleteScreen( BackHandler(onBack = onClickClose) Scaffold( + modifier = Modifier.statusBarsPadding(), topBar = { PaymentToolbar(onClickHome = onClickHome, onClickClose = onClickClose) }, ) { innerPadding -> val reservation = uiState.reservationDetail ?: return@Scaffold diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt index 003e23b47..9d500e6a9 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Icon @@ -50,6 +51,7 @@ fun HostedShowScreen( val uiState by viewModel.uiState.collectAsStateWithLifecycle() Scaffold( + modifier = Modifier.statusBarsPadding(), topBar = { BtBackAppBar( title = stringResource(R.string.hostedShowsTitle), diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt index 56343eee3..530e7d3d4 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -90,6 +91,7 @@ fun QrScanScreen( } Scaffold( + modifier = Modifier.statusBarsPadding(), topBar = { BtCloseableAppBar( title = uiState.showName, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsScreen.kt index 6e376cdb5..52399a76a 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsScreen.kt @@ -13,6 +13,7 @@ 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.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.HorizontalDivider @@ -63,6 +64,7 @@ fun ReservationsScreen( } Scaffold( + modifier = Modifier.statusBarsPadding(), topBar = { BtBackAppBar( title = stringResource(id = R.string.my_ticketing_history), diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt index 028b4bbab..b9f1388fc 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt @@ -24,6 +24,7 @@ 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.statusBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PagerState @@ -197,6 +198,7 @@ private fun TicketDetailScreen( } Scaffold( + modifier = modifier.statusBarsPadding(), topBar = { BtBackAppBar( title = stringResource(R.string.ticket_detail_title), @@ -216,7 +218,7 @@ private fun TicketDetailScreen( .padding(top = 16.dp) ) { Column( - modifier = modifier + modifier = Modifier .padding(innerPadding) .padding(horizontal = 29.dp) .verticalScroll(scrollState), From 6cbbc04db92b2a4703c303f35dcd6922ec253bdf Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sun, 22 Dec 2024 02:00:58 +0900 Subject: [PATCH 40/90] =?UTF-8?q?Boolti-343=20style:=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EB=B0=94=20=ED=8C=A8=EB=94=A9=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 회원 탈퇴 --- .../nexters/boolti/presentation/screen/signout/SignoutScreen.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutScreen.kt index 5d7c97da0..1b5cb0e71 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutScreen.kt @@ -3,6 +3,7 @@ package com.nexters.boolti.presentation.screen.signout import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -42,6 +43,7 @@ fun SignoutScreen( } Scaffold( + modifier = Modifier.systemBarsPadding(), topBar = { BtBackAppBar(title = stringResource(R.string.signout), onClickBack = navigateBack) }, bottomBar = { MainButton( From e69fe7114261b49a9de83c9037227bb0a427e255 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Wed, 25 Dec 2024 16:35:41 +0900 Subject: [PATCH 41/90] =?UTF-8?q?Boolti-343=20style:=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EB=B0=94=20=ED=8C=A8=EB=94=A9=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 공연 상세 - 티켓 상세 --- .../boolti/presentation/component/BtAppBar.kt | 9 +++++ .../boolti/presentation/screen/Main.kt | 37 +++++++++---------- .../screen/showdetail/ShowDetailScreen.kt | 10 +---- .../ticket/detail/TicketDetailScreen.kt | 33 ++++++++++------- .../boolti/presentation/util/TicketShape.kt | 3 -- 5 files changed, 46 insertions(+), 46 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/component/BtAppBar.kt b/presentation/src/main/java/com/nexters/boolti/presentation/component/BtAppBar.kt index 2a471ab0f..33453e14e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/component/BtAppBar.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/component/BtAppBar.kt @@ -8,6 +8,7 @@ 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.statusBarsPadding import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -35,12 +36,14 @@ fun BtAppBar( modifier: Modifier = Modifier, title: String = "", colors: BtAppBarColors = BtAppBarDefaults.appBarColors(), + statusBarPadding: Boolean = true, navigateButtons: @Composable (RowScope.() -> Unit)? = null, actionButtons: @Composable (RowScope.() -> Unit)? = null, ) { Row( modifier = modifier .background(color = colors.containerColor) + .let { if (statusBarPadding) it.statusBarsPadding() else it } .fillMaxWidth() .height(44.dp) .padding(horizontal = 8.dp), @@ -80,6 +83,7 @@ fun BtAppBar( modifier: Modifier = Modifier, title: String = "", colors: BtAppBarColors = BtAppBarDefaults.appBarColors(), + statusBarPadding: Boolean = true, navigationButtons: List Unit>> = emptyList(), actionButtons: List Unit>> = emptyList(), ) { @@ -87,6 +91,7 @@ fun BtAppBar( modifier = modifier, title = title, colors = colors, + statusBarPadding = statusBarPadding, navigateButtons = if (navigationButtons.isNotEmpty()) { { navigationButtons.forEach { (res, onClick) -> @@ -113,6 +118,7 @@ fun BtBackAppBar( modifier: Modifier = Modifier, title: String = "", colors: BtAppBarColors = BtAppBarDefaults.appBarColors(), + statusBarPadding: Boolean = true, onClickBack: () -> Unit, ) { BtAppBar( @@ -125,6 +131,7 @@ fun BtBackAppBar( ) }, colors = colors, + statusBarPadding = statusBarPadding, ) } @@ -133,6 +140,7 @@ fun BtCloseableAppBar( modifier: Modifier = Modifier, title: String = "", colors: BtAppBarColors = BtAppBarDefaults.appBarColors(), + statusBarPadding: Boolean = true, onClickClose: () -> Unit, ) { BtAppBar( @@ -146,6 +154,7 @@ fun BtCloseableAppBar( ) }, colors = colors, + statusBarPadding = statusBarPadding, ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 1d49edb14..7e074a8e4 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.remember @@ -67,26 +66,24 @@ fun Main(onClickQrScan: (showId: String, showName: String) -> Unit) { val snackbarHostState = remember { SnackbarHostState() } BooltiTheme { - Surface(modifier) { - Scaffold( - snackbarHost = { - ToastSnackbarHost( - modifier = Modifier.padding(bottom = 80.dp), - hostState = snackbarHostState, - ) - }, + Scaffold( + snackbarHost = { + ToastSnackbarHost( + modifier = Modifier.padding(bottom = 80.dp), + hostState = snackbarHostState, + ) + }, + ) { + CompositionLocalProvider( + LocalSnackbarController provides SnackbarController( + snackbarHostState, + scope + ) ) { - CompositionLocalProvider( - LocalSnackbarController provides SnackbarController( - snackbarHostState, - scope - ) - ) { - MainNavigation( - modifier = modifier, - onClickQrScan = onClickQrScan, - ) - } + MainNavigation( + modifier = modifier, + onClickQrScan = onClickQrScan, + ) } } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt index ba5448f1e..f3190f84e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt @@ -57,7 +57,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext @@ -81,7 +80,6 @@ import com.nexters.boolti.presentation.component.ShowInquiry import com.nexters.boolti.presentation.component.SmallButton import com.nexters.boolti.presentation.component.UserThumbnail import com.nexters.boolti.presentation.extension.asString -import com.nexters.boolti.presentation.extension.requireActivity import com.nexters.boolti.presentation.extension.showDateString import com.nexters.boolti.presentation.extension.showDateTimeString import com.nexters.boolti.presentation.screen.LocalSnackbarController @@ -144,9 +142,6 @@ fun ShowDetailScreen( val scope = rememberCoroutineScope() var showBottomSheet by remember { mutableStateOf(null) } - val window = LocalContext.current.requireActivity().window - window.statusBarColor = MaterialTheme.colorScheme.surface.toArgb() - LaunchedEffect(Unit) { viewModel.events.collect { event -> when (event) { @@ -176,7 +171,6 @@ fun ShowDetailScreen( navigateToReport = navigateToReport, ) }, - containerColor = MaterialTheme.colorScheme.background, ) { innerPadding -> Box( modifier = Modifier @@ -193,9 +187,7 @@ fun ShowDetailScreen( var buttonsHeight by remember { mutableStateOf(0.dp) } - LazyColumn( - modifier = Modifier, - ) { + LazyColumn { item { val paddingTop = if (showCountdownBanner) (38 + 40).dp else 16.dp diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt index b9f1388fc..7410599d1 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt @@ -24,7 +24,6 @@ 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.statusBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PagerState @@ -79,6 +78,7 @@ import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.compose.ui.zIndex import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavBackStackEntry @@ -192,13 +192,21 @@ private fun TicketDetailScreen( } } + LaunchedEffect(pullToRefreshState.isRefreshing) { + if (pullToRefreshState.isRefreshing) { + viewModel.refresh().invokeOnCompletion { + pullToRefreshState.endRefresh() + } + } + } + LaunchedEffect(pagerState) { snapshotFlow { pagerState.currentPage } .collect(viewModel::syncCurrentPage) } Scaffold( - modifier = modifier.statusBarsPadding(), + modifier = modifier, topBar = { BtBackAppBar( title = stringResource(R.string.ticket_detail_title), @@ -206,20 +214,19 @@ private fun TicketDetailScreen( ) }, ) { innerPadding -> - if (pullToRefreshState.isRefreshing) { - viewModel.refresh().invokeOnCompletion { - pullToRefreshState.endRefresh() - } - } - Box( modifier = Modifier + .padding(innerPadding) .nestedScroll(pullToRefreshState.nestedScrollConnection) - .padding(top = 16.dp) ) { + PullToRefreshContainer( + modifier = Modifier + .align(Alignment.TopCenter) + .zIndex(1f), + state = pullToRefreshState, + ) Column( modifier = Modifier - .padding(innerPadding) .padding(horizontal = 29.dp) .verticalScroll(scrollState), ) { @@ -236,6 +243,7 @@ private fun TicketDetailScreen( contentWidth = coordinates.size.width.toFloat() ticketSectionHeight = coordinates.size.height.toFloat() } + .padding(top = 16.dp) .clip(ticketShape) .border( width = 1.dp, @@ -386,10 +394,6 @@ private fun TicketDetailScreen( ) } } - PullToRefreshContainer( - modifier = Modifier.align(Alignment.TopCenter), - state = pullToRefreshState, - ) } } @@ -486,6 +490,7 @@ private fun QrCodes( .clip(RoundedCornerShape(8.dp)) .background(White), state = pagerState, + userScrollEnabled = pagerState.pageCount > 1, pageSpacing = 8.dp, ) { i -> val ticket = ticketGroup.tickets[i] diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/TicketShape.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/TicketShape.kt index 3738a55d6..77f1e5246 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/util/TicketShape.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/TicketShape.kt @@ -1,15 +1,12 @@ package com.nexters.boolti.presentation.util -import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Rect -import androidx.compose.ui.geometry.RoundRect import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Outline import androidx.compose.ui.graphics.Path import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.Density -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.LayoutDirection class TicketShape( From 846e074ff5d2a9ddca049e60f5960b1bf5929707 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Wed, 25 Dec 2024 16:43:08 +0900 Subject: [PATCH 42/90] =?UTF-8?q?Boolti-343=20style:=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EB=B0=94=20=ED=8C=A8=EB=94=A9=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - QR 화면 --- .../nexters/boolti/presentation/QrScanActivity.kt | 6 ++++++ .../boolti/presentation/screen/qr/QrFullScreen.kt | 12 ++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt b/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt index d892d3738..69742bd34 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt @@ -1,10 +1,13 @@ package com.nexters.boolti.presentation import android.Manifest +import android.graphics.Color import android.os.Bundle import android.view.KeyEvent import androidx.activity.ComponentActivity +import androidx.activity.SystemBarStyle import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle @@ -54,6 +57,9 @@ class QrScanActivity : ComponentActivity() { requestPermission(Manifest.permission.CAMERA, 100) + enableEdgeToEdge( + statusBarStyle = SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT), + ) setContent { BooltiTheme { QrScanScreen( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt index 4ae70449d..960f834c5 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt @@ -1,6 +1,5 @@ package com.nexters.boolti.presentation.screen.qr -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -13,6 +12,7 @@ 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.statusBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState @@ -69,7 +69,6 @@ fun NavGraphBuilder.QrFullScreen( } } -@OptIn(ExperimentalFoundationApi::class) @Composable fun QrFullScreen( modifier: Modifier = Modifier, @@ -81,12 +80,17 @@ fun QrFullScreen( Scaffold( modifier = modifier, - topBar = { Toolbar(onClose = onClose) }, + topBar = { + Toolbar( + modifier = Modifier.statusBarsPadding(), + onClose = onClose, + ) + }, ) { innerPadding -> Box( modifier = Modifier .fillMaxSize() - .background(Color.White) + .background(White) .padding(innerPadding), ) { HorizontalPager( From cd2ebd19d8cefe0f4754cb81dc0e652ef5b54770 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Wed, 25 Dec 2024 17:13:21 +0900 Subject: [PATCH 43/90] =?UTF-8?q?Boolti-343=20style:=20=ED=99=88(=EA=B3=B5?= =?UTF-8?q?=EC=97=B0=20=ED=94=BC=EB=93=9C)=20=EC=83=81=ED=83=9C=EB=B0=94?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/component/StatusBarCover.kt | 29 +++++++++++++++++++ .../presentation/screen/home/HomeScreen.kt | 2 +- .../presentation/screen/show/ShowScreen.kt | 6 ++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/component/StatusBarCover.kt diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/component/StatusBarCover.kt b/presentation/src/main/java/com/nexters/boolti/presentation/component/StatusBarCover.kt new file mode 100644 index 000000000..4ce70eafc --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/component/StatusBarCover.kt @@ -0,0 +1,29 @@ +package com.nexters.boolti.presentation.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.statusBarsIgnoringVisibility +import androidx.compose.foundation.layout.windowInsetsTopHeight +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.zIndex + +@OptIn(ExperimentalLayoutApi::class) +@Composable +fun StatusBarCover( + modifier: Modifier = Modifier, + color: Color = MaterialTheme.colorScheme.background, +) { + Box( + modifier = modifier + .zIndex(1f) + .background(color) + .fillMaxWidth() + .windowInsetsTopHeight(WindowInsets.statusBarsIgnoringVisibility), + ) +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt index 07f8b7a25..491272215 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt @@ -141,7 +141,7 @@ fun HomeScreen( ) ) { ShowScreen( - modifier = Modifier.padding(innerPadding), + modifier = Modifier.padding(bottom = innerPadding.calculateBottomPadding()), onClickShowItem = onClickShowItem, navigateToBusiness = navigateToBusiness, navigateToShowRegistration = { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt index 43cbb8ebc..2e7bd761d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -15,6 +16,7 @@ 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.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyVerticalGrid @@ -58,6 +60,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.component.BusinessInformation import com.nexters.boolti.presentation.component.ShowFeed +import com.nexters.boolti.presentation.component.StatusBarCover import com.nexters.boolti.presentation.extension.toPx import com.nexters.boolti.presentation.theme.Grey05 import com.nexters.boolti.presentation.theme.Grey15 @@ -68,6 +71,7 @@ import com.nexters.boolti.presentation.theme.marginHorizontal import com.nexters.boolti.presentation.theme.point1 import com.nexters.boolti.presentation.theme.point4 +@OptIn(ExperimentalLayoutApi::class) @Composable fun ShowScreen( navigateToBusiness: () -> Unit, @@ -116,6 +120,7 @@ fun ShowScreen( modifier = modifier.nestedScroll(nestedScrollConnection), contentAlignment = Alignment.TopCenter, ) { + StatusBarCover() LazyVerticalGrid( modifier = Modifier .padding(horizontal = marginHorizontal), @@ -191,6 +196,7 @@ fun ShowAppBar( Column( modifier = modifier .fillMaxWidth() + .statusBarsPadding() .background(MaterialTheme.colorScheme.background) .padding(horizontal = marginHorizontal) ) { From dbfc5849e1fedacbc0a64b9536506146d7313ee6 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Wed, 25 Dec 2024 17:16:33 +0900 Subject: [PATCH 44/90] =?UTF-8?q?Boolti-343=20chore:=20Theme=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=83=81=ED=83=9C=EB=B0=94=20=EC=83=89=EC=83=81=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/nexters/boolti/presentation/theme/Theme.kt | 9 --------- 1 file changed, 9 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/theme/Theme.kt b/presentation/src/main/java/com/nexters/boolti/presentation/theme/Theme.kt index 045d8bfc9..66b58cffe 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/theme/Theme.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/theme/Theme.kt @@ -49,15 +49,6 @@ fun BooltiTheme( darkTheme -> DarkColorScheme else -> LightColorScheme } - /*val view = LocalView.current - if (!view.isInEditMode) { - SideEffect { - val window = (view.context as Activity).window - window.statusBarColor = Grey95.toArgb() - window.navigationBarColor = Grey95.toArgb() - WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme - } - }*/ MaterialTheme( colorScheme = colorScheme, From 5f5c50ed3a77c74fe405daa2d8690c39164d9141 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Wed, 25 Dec 2024 17:24:19 +0900 Subject: [PATCH 45/90] =?UTF-8?q?Boolti-343=20refactor:=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20statusBarsPadding=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle/libs.versions.toml | 2 +- .../accountsetting/AccountSettingScreen.kt | 3 +-- .../screen/business/BusinessScreen.kt | 2 -- .../screen/payment/PaymentCompleteScreen.kt | 2 -- .../screen/qr/HostedShowScreen.kt | 2 -- .../presentation/screen/qr/QrScanScreen.kt | 24 ++++++++++++++----- .../screen/reservations/ReservationsScreen.kt | 2 -- .../presentation/screen/show/ShowScreen.kt | 2 +- 8 files changed, 21 insertions(+), 18 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2606355d1..7d0702ab5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] minSdk = "26" targetSdk = "34" -versionCode = "30" +versionCode = "31" versionName = "1.9.0" packageName = "com.nexters.boolti" compileSdk = "34" diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt index d2324f0a2..01aed7d8a 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt @@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll @@ -73,7 +72,7 @@ fun AccountSettingScreen( } AccountSettingScreen( - modifier = modifier.statusBarsPadding(), + modifier = modifier, userCode = user?.userCode ?: "", onClickBack = navigateBack, requireLogout = viewModel::logout, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessScreen.kt index 2cde8b4ce..cf1a7acaf 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessScreen.kt @@ -9,7 +9,6 @@ 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.statusBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll @@ -39,7 +38,6 @@ fun BusinessScreen( onBackPressed: () -> Unit, ) { Scaffold( - modifier = Modifier.statusBarsPadding(), contentColor = MaterialTheme.colorScheme.background, topBar = { BtBackAppBar( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentCompleteScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentCompleteScreen.kt index c8444e095..7108b2589 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentCompleteScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentCompleteScreen.kt @@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -60,7 +59,6 @@ fun PaymentCompleteScreen( BackHandler(onBack = onClickClose) Scaffold( - modifier = Modifier.statusBarsPadding(), topBar = { PaymentToolbar(onClickHome = onClickHome, onClickClose = onClickClose) }, ) { innerPadding -> val reservation = uiState.reservationDetail ?: return@Scaffold diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt index 9d500e6a9..003e23b47 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt @@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Icon @@ -51,7 +50,6 @@ fun HostedShowScreen( val uiState by viewModel.uiState.collectAsStateWithLifecycle() Scaffold( - modifier = Modifier.statusBarsPadding(), topBar = { BtBackAppBar( title = stringResource(R.string.hostedShowsTitle), diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt index 530e7d3d4..9d42a9289 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt @@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -76,9 +75,20 @@ fun QrScanScreen( val (iconId, errMessage) = when (event) { is QrScanEvent.ScanError -> { when (event.errorType) { - QrErrorType.ShowNotToday -> Pair(R.drawable.ic_warning, notTodayErrMessage) - QrErrorType.UsedTicket -> Pair(R.drawable.ic_error, usedTicketErrMessage) - QrErrorType.TicketNotFound -> Pair(R.drawable.ic_error, notMatchedErrMessage) + QrErrorType.ShowNotToday -> Pair( + R.drawable.ic_warning, + notTodayErrMessage + ) + + QrErrorType.UsedTicket -> Pair( + R.drawable.ic_error, + usedTicketErrMessage + ) + + QrErrorType.TicketNotFound -> Pair( + R.drawable.ic_error, + notMatchedErrMessage + ) } } @@ -91,7 +101,6 @@ fun QrScanScreen( } Scaffold( - modifier = Modifier.statusBarsPadding(), topBar = { BtCloseableAppBar( title = uiState.showName, @@ -174,7 +183,10 @@ private fun EntryCodeDialog( onDismiss: () -> Unit, ) { BTDialog(showCloseButton = false, onDismiss = onDismiss, onClickPositiveButton = onDismiss) { - Text(text = stringResource(R.string.manager_code), style = MaterialTheme.typography.titleLarge) + Text( + text = stringResource(R.string.manager_code), + style = MaterialTheme.typography.titleLarge + ) Text( modifier = Modifier .padding(top = 24.dp) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsScreen.kt index 52399a76a..6e376cdb5 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsScreen.kt @@ -13,7 +13,6 @@ 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.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.HorizontalDivider @@ -64,7 +63,6 @@ fun ReservationsScreen( } Scaffold( - modifier = Modifier.statusBarsPadding(), topBar = { BtBackAppBar( title = stringResource(id = R.string.my_ticketing_history), diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt index 2e7bd761d..5d45a70e9 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt @@ -196,8 +196,8 @@ fun ShowAppBar( Column( modifier = modifier .fillMaxWidth() - .statusBarsPadding() .background(MaterialTheme.colorScheme.background) + .statusBarsPadding() .padding(horizontal = marginHorizontal) ) { Spacer(modifier = Modifier.height(40.dp)) From 206d8a622c6a9e27700ad9320c9f6144a5e65d02 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Wed, 25 Dec 2024 18:23:51 +0900 Subject: [PATCH 46/90] =?UTF-8?q?Boolti-347=20feat:=20=EB=B8=8C=EB=A6=BF?= =?UTF-8?q?=EC=A7=80=20-=20=ED=99=94=EB=A9=B4=20=EC=9D=B4=EB=8F=99,=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EA=B0=B1=EC=8B=A0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/Main.kt | 2 + .../ShowRegistrationNavigation.kt | 4 ++ .../ShowRegistrationScreen.kt | 38 +++++++++---------- .../ShowRegistrationViewModel.kt | 10 +---- .../util/bridge/BridgeCallbackHandler.kt | 7 +++- .../presentation/util/bridge/BridgeManager.kt | 26 ++++++++++++- 6 files changed, 56 insertions(+), 31 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 32c7e7061..6d4e13199 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -298,6 +298,8 @@ fun MainNavigation(modifier: Modifier, onClickQrScan: (showId: String, showName: addShowRegistration( modifier = modifier, popBackStack = navController::popBackStack, + navigateTo = navController::navigateTo, + navigateToHome = navController::navigateToHome, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationNavigation.kt index 991e46599..44cc95776 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationNavigation.kt @@ -8,6 +8,8 @@ import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.addShowRegistration( modifier: Modifier = Modifier, popBackStack: () -> Unit, + navigateTo: (route: String) -> Unit, + navigateToHome: () -> Unit, ) { composable( route = MainDestination.ShowRegistration.route, @@ -15,6 +17,8 @@ fun NavGraphBuilder.addShowRegistration( ShowRegistrationScreen( modifier = modifier, onClickBack = popBackStack, + navigateTo = navigateTo, + navigateToHome = navigateToHome, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt index b44784e01..4d021a2b7 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.net.Uri import android.webkit.CookieManager import android.webkit.ValueCallback -import android.webkit.WebStorage import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts @@ -39,9 +38,8 @@ import com.nexters.boolti.presentation.component.BtCircularProgressIndicator import com.nexters.boolti.presentation.component.BtWebView import com.nexters.boolti.presentation.util.bridge.BridgeCallbackHandler import com.nexters.boolti.presentation.util.bridge.BridgeManager +import com.nexters.boolti.presentation.util.bridge.NavigateOption import com.nexters.boolti.presentation.util.bridge.TokenDto -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.launch import timber.log.Timber @@ -50,6 +48,8 @@ import timber.log.Timber fun ShowRegistrationScreen( modifier: Modifier = Modifier, onClickBack: () -> Unit, + navigateTo: (route: String) -> Unit, + navigateToHome: () -> Unit, viewModel: ShowRegistrationViewModel = hiltViewModel(), ) { var filePathCallback: ValueCallback>? by remember { mutableStateOf(null) } @@ -72,29 +72,27 @@ fun ShowRegistrationScreen( webView?.setBridgeManager( BridgeManager( callbackHandler = object : BridgeCallbackHandler { - override fun fetchToken(): TokenDto = TokenDto("test") + override suspend fun fetchToken(): TokenDto { + val accessToken = viewModel.refreshAndGetToken() + return TokenDto(token = accessToken.token) + } + + override fun navigateTo(route: String, navigateOption: NavigateOption) { + when (navigateOption) { + NavigateOption.PUSH -> navigateTo(route) + NavigateOption.HOME -> navigateToHome() + NavigateOption.CLOSE_AND_OPEN -> { + onClickBack() + navigateTo(route) + } + } + } }, scope = scope, ) ) } - LaunchedEffect(Unit) { - CookieManager.getInstance().removeAllCookies(null) - WebStorage.getInstance().deleteAllData() - - viewModel.tokens - .filterNotNull() - .filter { it.isLoggedIn } - .collect { tokens -> - with(CookieManager.getInstance()) { - setCookie(url, "x-access-token=${tokens.accessToken}") - setCookie(url, "x-refresh-token=${tokens.refreshToken}") - flush() - } - } - } - BackHandler { if (webView?.canGoBack() == true) webView?.goBack() else showExitDialog = true } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationViewModel.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationViewModel.kt index 8861822e3..040530e52 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationViewModel.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationViewModel.kt @@ -1,20 +1,14 @@ package com.nexters.boolti.presentation.screen.showregistration import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope import com.nexters.boolti.domain.repository.AuthRepository -import com.nexters.boolti.presentation.extension.stateInUi import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.first import javax.inject.Inject @HiltViewModel class ShowRegistrationViewModel @Inject constructor( val authRepository: AuthRepository, ) : ViewModel() { - val tokens = authRepository.getTokens() - .stateInUi(viewModelScope, null) - - fun refreshToken() { - authRepository.refreshToken() - } + suspend fun refreshAndGetToken() = authRepository.refreshToken().first() } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt index b13ffe815..69cc25f15 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt @@ -8,7 +8,12 @@ import kotlinx.serialization.Serializable * 새로운 액션이 정의되는 경우 [BridgeManager.handleIncomingData] 도 함께 수정되어야 한다. */ interface BridgeCallbackHandler { - fun fetchToken(): TokenDto + suspend fun fetchToken(): TokenDto + fun navigateTo(route: String, navigateOption: NavigateOption = NavigateOption.PUSH) +} + +enum class NavigateOption { + PUSH, HOME, CLOSE_AND_OPEN, } @Serializable diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt index 469a57ea2..7e67b9d8e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt @@ -1,14 +1,19 @@ package com.nexters.boolti.presentation.util.bridge +import com.nexters.boolti.presentation.screen.MainDestination import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.encodeToJsonElement +import kotlinx.serialization.json.jsonObject +import timber.log.Timber class BridgeManager( private val callbackHandler: BridgeCallbackHandler, @@ -33,8 +38,25 @@ class BridgeManager( when (data.command) { CommandType.REQUEST_TOKEN -> { - val token = callbackHandler.fetchToken() - callbackToWeb(data, json.encodeToJsonElement(token)) + scope.launch { + val token = callbackHandler.fetchToken() + callbackToWeb(data, json.encodeToJsonElement(token)) + } + } + + CommandType.NAVIGATE_TO_SHOW_DETAIL -> { + data.data?.jsonObject?.get("showId")?.toString()?.let { showId -> + scope.launch { + withContext(Dispatchers.Main) { + Timber.tag("bridge").d("공연 상세 화면으로 이동 $showId") + callbackHandler.navigateTo( + route = MainDestination.ShowDetail.createRoute(showId), + navigateOption = NavigateOption.CLOSE_AND_OPEN, + ) + } + } + } ?: Timber.tag("bridge").d("공연 상세 화면으로 이동 실패: showId 없음") + callbackToWeb(data, null) } else -> callbackToWeb(data, null) From 7ff99bafb338d61d7f35e1bab8c0da6ec4f22801 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Wed, 25 Dec 2024 18:26:54 +0900 Subject: [PATCH 47/90] =?UTF-8?q?Boolti-347=20feat:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=9C=A0=EC=A0=80=EB=A7=8C=20=EA=B3=B5=EC=97=B0=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/nexters/boolti/presentation/screen/my/MyScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt index 4aafe2cdb..5678c1d06 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt @@ -77,7 +77,7 @@ fun MyScreen( onClickHeaderButton = if (user != null) navigateToProfile else requireLogin, onClickAccountSetting = if (user != null) onClickAccountSetting else requireLogin, onClickReservations = if (user != null) navigateToReservations else requireLogin, - onClickRegisterShow = navigateToShowRegistration, + onClickRegisterShow = if (user != null) navigateToShowRegistration else requireLogin, onClickQrScan = if (user != null) onClickQrScan else requireLogin, ) } From 622da9e32e844ea7b4e3f37f3ea15744ba358eff Mon Sep 17 00:00:00 2001 From: mangbaam Date: Fri, 27 Dec 2024 19:20:40 +0900 Subject: [PATCH 48/90] =?UTF-8?q?Boolti-355=20style:=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=20=EC=82=AC=EC=A7=84=20=EC=98=81=EC=97=AD=20UI=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../screen/profile/ProfileScreen.kt | 271 +++++++++++------- 1 file changed, 173 insertions(+), 98 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt index 0a6750db7..70e3fa948 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt @@ -11,10 +11,10 @@ import androidx.compose.foundation.layout.FlowRow 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.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape @@ -37,17 +37,22 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import coil.compose.AsyncImage import com.nexters.boolti.domain.model.Link import com.nexters.boolti.domain.model.Sns import com.nexters.boolti.domain.model.User @@ -57,7 +62,6 @@ import com.nexters.boolti.presentation.component.BTDialog import com.nexters.boolti.presentation.component.BtAppBar import com.nexters.boolti.presentation.component.BtAppBarDefaults import com.nexters.boolti.presentation.component.ShowItem -import com.nexters.boolti.presentation.component.UserThumbnail import com.nexters.boolti.presentation.extension.toValidUrlString import com.nexters.boolti.presentation.screen.LocalSnackbarController import com.nexters.boolti.presentation.theme.BooltiTheme @@ -69,6 +73,7 @@ import com.nexters.boolti.presentation.theme.marginHorizontal import com.nexters.boolti.presentation.theme.point3 import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.emptyFlow @Composable fun ProfileScreen( @@ -142,74 +147,32 @@ fun ProfileScreen( Scaffold( modifier = modifier, - topBar = { - BtAppBar( - title = stringResource(R.string.profile_title), - colors = BtAppBarDefaults.appBarColors(containerColor = MaterialTheme.colorScheme.surface), - navigateButtons = { - BtAppBarDefaults.AppBarIconButton( - iconRes = R.drawable.ic_arrow_back, - onClick = onClickBack, - ) - }, - actionButtons = { - if (isMine) { - BtAppBarDefaults.AppBarTextButton( - label = stringResource(R.string.edit), - onClick = navigateToProfileEdit, - ) - } else { - BtAppBarDefaults.AppBarIconButton( - iconRes = R.drawable.ic_verticle_more, - description = stringResource(R.string.description_more_menu), - onClick = { showContextMenu = true }, - ) - } - }, - ) - if (showContextMenu) { - Box( - modifier = Modifier - .fillMaxWidth() - .wrapContentSize(Alignment.TopEnd), - ) { - DropdownMenu( - modifier = Modifier.background(Grey20), - expanded = showContextMenu, - onDismissRequest = { showContextMenu = false }, - ) { - DropdownMenuItem( - text = { - Text( - text = stringResource(id = R.string.report), - color = Color.Black, - ) - }, - onClick = { - showContextMenu = false - snackbarHostState.showMessage(reportFinishedMessage) - }, - ) - } - } - } - } ) { innerPadding -> Column( modifier = modifier .verticalScroll(scrollState) .padding(innerPadding), ) { - ProfileHeader( - user = user, - onClickSns = { sns -> - try { - uriHandler.openUri(sns.url.toValidUrlString()) - } catch (e: ActivityNotFoundException) { - snackbarHostState.showMessage(invalidUrlMsg) - } - }, - ) + Box( + modifier = Modifier.fillMaxWidth() + ) { + ProfileAppBar( + onClickBack = onClickBack, + isMine = isMine, + navigateToProfileEdit = navigateToProfileEdit, + onReportFinished = { snackbarHostState.showMessage(reportFinishedMessage) }, + ) + ProfileHeader( + user = user, + onClickSns = { sns -> + try { + uriHandler.openUri(sns.url.toValidUrlString()) + } catch (e: ActivityNotFoundException) { + snackbarHostState.showMessage(invalidUrlMsg) + } + }, + ) + } if (user.link.isNotEmpty() || user.performedShow.isNotEmpty()) { Spacer(Modifier.size(8.dp)) @@ -298,6 +261,68 @@ fun ProfileScreen( } } +@Composable +private fun ProfileAppBar( + onClickBack: () -> Unit, + isMine: Boolean, + navigateToProfileEdit: () -> Unit, + onReportFinished: () -> Unit, +) { + var showContextMenu by rememberSaveable { mutableStateOf(false) } + + BtAppBar( + modifier = Modifier.zIndex(1f), + title = stringResource(R.string.profile_title), + colors = BtAppBarDefaults.appBarColors(containerColor = Color.Transparent), + navigateButtons = { + BtAppBarDefaults.AppBarIconButton( + iconRes = R.drawable.ic_arrow_back, + onClick = onClickBack, + ) + }, + actionButtons = { + if (isMine) { + BtAppBarDefaults.AppBarTextButton( + label = stringResource(R.string.edit), + onClick = navigateToProfileEdit, + ) + } else { + BtAppBarDefaults.AppBarIconButton( + iconRes = R.drawable.ic_verticle_more, + description = stringResource(R.string.description_more_menu), + onClick = { showContextMenu = true }, + ) + } + }, + ) + if (showContextMenu) { + Box( + modifier = Modifier + .fillMaxWidth() + .wrapContentSize(Alignment.TopEnd), + ) { + DropdownMenu( + modifier = Modifier.background(Grey20), + expanded = showContextMenu, + onDismissRequest = { showContextMenu = false }, + ) { + DropdownMenuItem( + text = { + Text( + text = stringResource(id = R.string.report), + color = Color.Black, + ) + }, + onClick = { + showContextMenu = false + onReportFinished() + }, + ) + } + } + } +} + @OptIn(ExperimentalLayoutApi::class) @Composable private fun ProfileHeader( @@ -306,48 +331,67 @@ private fun ProfileHeader( onClickSns: (Sns) -> Unit, ) { val shape = RoundedCornerShape( - bottomStart = 12.dp, - bottomEnd = 12.dp, + bottomStart = 20.dp, + bottomEnd = 20.dp, ) - Column( - modifier = modifier - .fillMaxWidth() - .wrapContentHeight() - .clip(shape) - .background(MaterialTheme.colorScheme.surface) - .padding(horizontal = marginHorizontal) - .padding(bottom = 32.dp), + Box( + modifier = modifier.background(MaterialTheme.colorScheme.background), ) { - UserThumbnail( - modifier = Modifier.padding(top = 40.dp), - size = 70.dp, + val defaultImage = R.drawable.ic_fallback_profile + AsyncImage( + modifier = Modifier + .fillMaxWidth() + .aspectRatio(1f), model = user.photo, + contentScale = ContentScale.Crop, + placeholder = painterResource(id = defaultImage), + fallback = painterResource(id = defaultImage), + contentDescription = stringResource(R.string.description_user_thumbnail), ) - Text( - modifier = Modifier.padding(top = 20.dp), - text = user.nickname, - style = point3, - fontWeight = FontWeight.Normal, - color = MaterialTheme.colorScheme.onSurface, - maxLines = 2, - overflow = TextOverflow.Ellipsis, + Box( + modifier = Modifier + .fillMaxWidth() + .aspectRatio(1f) + .background( + brush = Brush.verticalGradient( + listOf(Color(0x33121318), Color(0xFF121318)), + ), + ) ) - if (user.introduction.isNotBlank()) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(marginHorizontal) + .padding(top = 232.dp, bottom = 32.dp) + .clip(shape), + ) { Text( - modifier = Modifier.padding(top = 2.dp), - text = user.introduction, - color = Grey30, - style = MaterialTheme.typography.bodyLarge, + modifier = Modifier.padding(top = 20.dp), + text = user.nickname, + style = point3, + fontWeight = FontWeight.Normal, + color = MaterialTheme.colorScheme.onSurface, + maxLines = 2, + overflow = TextOverflow.Ellipsis, ) - } - if (user.sns.isNotEmpty()) { - FlowRow( - modifier = Modifier.padding(top = 16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - user.sns.forEach { sns -> SnsChip(sns) { onClickSns(sns) } } + + if (user.introduction.isNotBlank()) { + Text( + modifier = Modifier.padding(top = 2.dp), + text = user.introduction, + color = Grey30, + style = MaterialTheme.typography.bodyLarge, + ) + } + if (user.sns.isNotEmpty()) { + FlowRow( + modifier = Modifier.padding(top = 20.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + user.sns.forEach { sns -> SnsChip(sns) { onClickSns(sns) } } + } } } } @@ -472,3 +516,34 @@ private fun SectionPreview() { ) {} } } + +@Preview +@Composable +private fun ProfileScreenPreview() { + val user = User.My( + id = "", + nickname = "mangbaam", + email = "mangbaam@boolti.com", + photo = null, + userCode = "oratio", + introduction = "안녕하세요", + sns = listOf( + Sns("1", Sns.SnsType.INSTAGRAM, "hey__suun"), + Sns("1", Sns.SnsType.YOUTUBE, "tune_official"), + ), + link = listOf(), + performedShow = listOf(), + ) + BooltiTheme { + ProfileScreen( + user = user, + isMine = false, + event = emptyFlow(), + onClickBack = {}, + navigateToProfileEdit = {}, + navigateToLinks = {}, + navigateToShow = {}, + navigateToPerformedShows = {}, + ) + } +} From 98d2d3d641375cefeda32e25a0e68e6fa8a8a4d2 Mon Sep 17 00:00:00 2001 From: algosketch Date: Mon, 30 Dec 2024 00:13:17 +0900 Subject: [PATCH 49/90] =?UTF-8?q?Revert=20"fix=20:=20[=EB=94=A5=20?= =?UTF-8?q?=EB=A7=81=ED=81=AC]=20=EC=9D=B4=ED=9B=84=20=EB=92=A4=EB=A1=9C?= =?UTF-8?q?=20=EA=B0=80=EA=B8=B0=EC=8B=9C=20=ED=99=88=EC=9D=B4=20=EC=95=84?= =?UTF-8?q?=EB=8B=8C=20=ED=8B=B0=EC=BC=93=20=ED=83=AD=EC=9D=B4=20=EB=82=98?= =?UTF-8?q?=EC=98=A4=EB=8A=94=20=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/screen/home/HomeScreen.kt | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt index 878bf106e..42f5dc38f 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt @@ -1,6 +1,5 @@ package com.nexters.boolti.presentation.screen.home -import android.content.Context import android.content.Intent import android.net.Uri import android.widget.Toast @@ -78,8 +77,6 @@ fun HomeScreen( var dialog: GiftStatus? by rememberSaveable { mutableStateOf(null) } - removeInvalidDeepLink(LocalContext.current) - LaunchedEffect(Unit) { viewModel.events.collect { event -> when (event) { @@ -209,19 +206,6 @@ fun HomeScreen( } } -/** - * issue #209를 해결하기 위한 메서드. - * 처리하지 말아야 할 deep link가 부적절한 destination과 match되는 것을 방지하기 위함. - */ -private fun removeInvalidDeepLink(context: Context) { - runCatching { - val intent = context.requireActivity().intent - if (intent.action == null) return - val deepLink = intent.action!! - if (!deepLink.contains("home")) intent.setAction(null) - } -} - @Stable private enum class Destination( val route: String, From 49acd1239390c84d9e115c870c4e2a3e92973c17 Mon Sep 17 00:00:00 2001 From: HamBP Date: Mon, 30 Dec 2024 00:47:46 +0900 Subject: [PATCH 50/90] chore: version up --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1909e7caf..64377c36d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] minSdk = "26" targetSdk = "34" -versionCode = "31" -versionName = "1.9.1" +versionCode = "32" +versionName = "1.9.2" packageName = "com.nexters.boolti" compileSdk = "34" targetJvm = "17" From 781d27808672193815c95cb45f9c01ed26133b3f Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 00:26:34 +0900 Subject: [PATCH 51/90] =?UTF-8?q?Boolti-355=20style:=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=20=EC=82=AC=EC=A7=84=20=EC=98=81=EC=97=AD=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../screen/profile/ProfileScreen.kt | 122 +++++++++++------- .../res/drawable/ic_profile_placeholder.png | Bin 0 -> 81961 bytes 2 files changed, 76 insertions(+), 46 deletions(-) create mode 100644 presentation/src/main/res/drawable/ic_profile_placeholder.png diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt index 70e3fa948..33c830d07 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt @@ -1,6 +1,7 @@ package com.nexters.boolti.presentation.screen.profile import android.content.ActivityNotFoundException +import androidx.compose.animation.animateColorAsState import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -11,8 +12,9 @@ import androidx.compose.foundation.layout.FlowRow 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.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentSize @@ -32,6 +34,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -41,6 +44,9 @@ import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -62,6 +68,7 @@ import com.nexters.boolti.presentation.component.BTDialog import com.nexters.boolti.presentation.component.BtAppBar import com.nexters.boolti.presentation.component.BtAppBarDefaults import com.nexters.boolti.presentation.component.ShowItem +import com.nexters.boolti.presentation.extension.toDp import com.nexters.boolti.presentation.extension.toValidUrlString import com.nexters.boolti.presentation.screen.LocalSnackbarController import com.nexters.boolti.presentation.theme.BooltiTheme @@ -128,14 +135,20 @@ fun ProfileScreen( val invalidUrlMsg = stringResource(R.string.invalid_link) val scrollState = rememberScrollState() + val appBarBgColor by animateColorAsState( + targetValue = if (scrollState.canScrollBackward) { + MaterialTheme.colorScheme.surface + } else { + Color.Transparent + }, + label = "appBarBgColor", + ) var backDialogMessage by rememberSaveable { mutableStateOf(null) } val invalidUserMessage = stringResource(R.string.profile_invalid_user_message) val withdrawUserMessage = stringResource(R.string.profile_withdraw_user_message) val reportFinishedMessage = stringResource(R.string.report_finished) - var showContextMenu by rememberSaveable { mutableStateOf(false) } - LaunchedEffect(event) { event.collectLatest { when (it) { @@ -148,31 +161,29 @@ fun ProfileScreen( Scaffold( modifier = modifier, ) { innerPadding -> + ProfileAppBar( + onClickBack = onClickBack, + isMine = isMine, + bgColor = appBarBgColor, + navigateToProfileEdit = navigateToProfileEdit, + onReportFinished = { snackbarHostState.showMessage(reportFinishedMessage) }, + ) Column( modifier = modifier .verticalScroll(scrollState) .padding(innerPadding), ) { - Box( - modifier = Modifier.fillMaxWidth() - ) { - ProfileAppBar( - onClickBack = onClickBack, - isMine = isMine, - navigateToProfileEdit = navigateToProfileEdit, - onReportFinished = { snackbarHostState.showMessage(reportFinishedMessage) }, - ) - ProfileHeader( - user = user, - onClickSns = { sns -> - try { - uriHandler.openUri(sns.url.toValidUrlString()) - } catch (e: ActivityNotFoundException) { - snackbarHostState.showMessage(invalidUrlMsg) - } - }, - ) - } + ProfileHeader( + modifier = Modifier.fillMaxWidth(), + user = user, + onClickSns = { sns -> + try { + uriHandler.openUri(sns.url.toValidUrlString()) + } catch (e: ActivityNotFoundException) { + snackbarHostState.showMessage(invalidUrlMsg) + } + }, + ) if (user.link.isNotEmpty() || user.performedShow.isNotEmpty()) { Spacer(Modifier.size(8.dp)) @@ -265,6 +276,7 @@ fun ProfileScreen( private fun ProfileAppBar( onClickBack: () -> Unit, isMine: Boolean, + bgColor: Color, navigateToProfileEdit: () -> Unit, onReportFinished: () -> Unit, ) { @@ -273,7 +285,7 @@ private fun ProfileAppBar( BtAppBar( modifier = Modifier.zIndex(1f), title = stringResource(R.string.profile_title), - colors = BtAppBarDefaults.appBarColors(containerColor = Color.Transparent), + colors = BtAppBarDefaults.appBarColors(containerColor = bgColor), navigateButtons = { BtAppBarDefaults.AppBarIconButton( iconRes = R.drawable.ic_arrow_back, @@ -334,37 +346,55 @@ private fun ProfileHeader( bottomStart = 20.dp, bottomEnd = 20.dp, ) + var contentHeight by remember { + mutableStateOf(0.dp) + } + val screenWidth = LocalConfiguration.current.screenWidthDp.dp + val density = LocalDensity.current + val profileHeight = contentHeight.coerceAtMost(screenWidth) + + val defaultProfile = painterResource(R.drawable.ic_profile_placeholder) + Box( - modifier = modifier.background(MaterialTheme.colorScheme.background), + modifier = modifier + .clip(shape) + .background(MaterialTheme.colorScheme.surface), ) { - val defaultImage = R.drawable.ic_fallback_profile - AsyncImage( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(1f), - model = user.photo, - contentScale = ContentScale.Crop, - placeholder = painterResource(id = defaultImage), - fallback = painterResource(id = defaultImage), - contentDescription = stringResource(R.string.description_user_thumbnail), - ) Box( modifier = Modifier .fillMaxWidth() - .aspectRatio(1f) - .background( - brush = Brush.verticalGradient( - listOf(Color(0x33121318), Color(0xFF121318)), + .height(profileHeight), + ) { + AsyncImage( + modifier = Modifier.fillMaxSize(), + model = user.photo, + contentScale = ContentScale.Crop, + placeholder = defaultProfile, + fallback = defaultProfile, + contentDescription = stringResource(R.string.description_user_thumbnail), + ) + Box( + modifier = Modifier + .fillMaxSize() + .background( + brush = Brush.verticalGradient( + listOf(Color(0x33121318), Color(0xFF121318)), + ), ), - ) - ) + ) + } Column( modifier = Modifier .fillMaxWidth() - .padding(marginHorizontal) - .padding(top = 232.dp, bottom = 32.dp) - .clip(shape), + .onSizeChanged { + contentHeight = it.height.toDp(density) + } + .padding(horizontal = marginHorizontal) + .padding( + top = 188.dp, + bottom = 32.dp, + ), // TODO StatusBar 까지 확장되면 StatusBar 높이 추가되어야 함 ) { Text( modifier = Modifier.padding(top = 20.dp), @@ -526,7 +556,7 @@ private fun ProfileScreenPreview() { email = "mangbaam@boolti.com", photo = null, userCode = "oratio", - introduction = "안녕하세요", + introduction = "안녕하세요\n안녕하세요\n안녕하세요\n안녕하세요\n안녕하세요\n안녕하세요\n안녕하세요\n안녕하세요\n", sns = listOf( Sns("1", Sns.SnsType.INSTAGRAM, "hey__suun"), Sns("1", Sns.SnsType.YOUTUBE, "tune_official"), diff --git a/presentation/src/main/res/drawable/ic_profile_placeholder.png b/presentation/src/main/res/drawable/ic_profile_placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..56b31eda914e28f1a6f08d3a4c18fb5fcd59ce86 GIT binary patch literal 81961 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
  • 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 ckNi2g^i?XQ$1g2<{d9=(V~s~y4_ Date: Tue, 31 Dec 2024 00:45:04 +0900 Subject: [PATCH 52/90] =?UTF-8?q?Boolti-343=20fix:=20=EB=A8=B8=EC=A7=80=20?= =?UTF-8?q?=EC=B6=A9=EB=8F=8C=20=ED=95=B4=EA=B2=B0=20=ED=9B=84=20=EA=B3=B5?= =?UTF-8?q?=EC=97=B0=20=EC=83=81=EC=84=B8=20UI=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/showdetail/ShowDetailScreen.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt index 15339b53c..6d23c32f4 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt @@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentSize @@ -156,7 +157,7 @@ fun ShowDetailScreen( CompositionLocalProvider(LocalOverscrollConfiguration provides null) { Scaffold( - modifier = modifier, + modifier = modifier.navigationBarsPadding(), topBar = { ShowDetailAppBar( showDetail = uiState.showDetail, From 7e1749f12c700ab17db5384b190c3846b7da677c Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 00:49:00 +0900 Subject: [PATCH 53/90] =?UTF-8?q?Boolti-343=20style:=20QR=20=EC=8A=A4?= =?UTF-8?q?=EC=BA=94=20=ED=99=94=EB=A9=B4=20=ED=95=98=EB=8B=A8=20=EC=98=81?= =?UTF-8?q?=EC=97=AD=20=ED=8C=A8=EB=94=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt index 9d42a9289..966433862 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape @@ -101,6 +102,7 @@ fun QrScanScreen( } Scaffold( + modifier = Modifier.navigationBarsPadding(), topBar = { BtCloseableAppBar( title = uiState.showName, From b9dda465aca1467e95457128b56b474ccbcee4e7 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 00:59:26 +0900 Subject: [PATCH 54/90] =?UTF-8?q?Boolti-343=20style:=20=ED=83=88=ED=87=B4,?= =?UTF-8?q?=20=ED=99=98=EB=B6=88=20=EC=82=AC=EC=9C=A0=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=20=ED=85=8D=EC=8A=A4=ED=8A=B8=ED=95=84=EB=93=9C=20=EC=B5=9C?= =?UTF-8?q?=EC=86=8C=20=EB=86=92=EC=9D=B4=20=EC=88=98=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=ED=99=98=EB=B6=88=20=EC=82=AC=EC=9C=A0=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=20=ED=99=94=EB=A9=B4=EC=97=90=EC=84=9C=20clickable=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/screen/refund/ReasonPage.kt | 25 +++++-------------- .../screen/refund/RefundScreen.kt | 7 ++---- .../screen/signout/SignoutReason.kt | 3 ++- .../screen/signout/SignoutScreen.kt | 4 +-- 4 files changed, 12 insertions(+), 27 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/ReasonPage.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/ReasonPage.kt index d0f0a8667..72bfc3a7f 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/ReasonPage.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/ReasonPage.kt @@ -1,17 +1,13 @@ package com.nexters.boolti.presentation.screen.refund -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.nexters.boolti.presentation.R @@ -27,17 +23,7 @@ fun ReasonPage( onNextClick: () -> Unit, modifier: Modifier = Modifier, ) { - val keyboardController = LocalSoftwareKeyboardController.current - val interactionSource = remember { MutableInteractionSource() } - - Column( - modifier = modifier.clickable( - interactionSource = interactionSource, - indication = null - ) { - keyboardController?.hide() - } - ) { + Column(modifier = modifier) { Text( modifier = Modifier .padding(top = 20.dp) @@ -48,10 +34,11 @@ fun ReasonPage( BTTextField( modifier = Modifier .fillMaxWidth() + .heightIn(max = 300.dp) .padding(horizontal = marginHorizontal) - .height(160.dp) .padding(top = 20.dp), text = reason, + minHeight = 160.dp, onValueChanged = onReasonChanged, placeholder = stringResource(id = R.string.refund_reason_hint), ) @@ -64,7 +51,7 @@ fun ReasonPage( .padding(bottom = 8.dp), onClick = onNextClick, enabled = reason.isNotBlank(), - label = stringResource(id = R.string.next) + label = stringResource(id = R.string.next), ) } -} \ No newline at end of file +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundScreen.kt index 9aae927fb..248d862fa 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundScreen.kt @@ -1,12 +1,12 @@ package com.nexters.boolti.presentation.screen.refund -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState @@ -29,11 +29,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.nexters.boolti.domain.model.PaymentType import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.component.BTDialog import com.nexters.boolti.presentation.component.BtBackAppBar -import com.nexters.boolti.presentation.extension.cardCodeToCompanyName import com.nexters.boolti.presentation.extension.getPaymentString import com.nexters.boolti.presentation.screen.LocalSnackbarController import com.nexters.boolti.presentation.theme.Grey15 @@ -41,7 +39,6 @@ import com.nexters.boolti.presentation.theme.Grey30 import com.nexters.boolti.presentation.theme.Grey80 import kotlinx.coroutines.launch -@OptIn(ExperimentalFoundationApi::class) @Composable fun RefundScreen( isGift: Boolean, @@ -84,7 +81,7 @@ fun RefundScreen( onClickBack = onBackPressed, ) }, - modifier = Modifier, + modifier = Modifier.navigationBarsPadding(), ) { innerPadding -> val reservation = uiState.reservation ?: return@Scaffold diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutReason.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutReason.kt index 4a2f5697b..6f5ca1065 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutReason.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutReason.kt @@ -39,9 +39,10 @@ fun SignoutReason( BTTextField( modifier = Modifier .fillMaxWidth() - .heightIn(min = 160.dp, max = 300.dp) + .heightIn(max = 300.dp) .padding(horizontal = marginHorizontal), text = reason, + minHeight = 160.dp, placeholder = stringResource(R.string.signout_reason_placeholder), onValueChanged = viewModel::setReason, ) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutScreen.kt index 1b5cb0e71..d4047f498 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutScreen.kt @@ -2,8 +2,8 @@ package com.nexters.boolti.presentation.screen.signout import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -43,7 +43,7 @@ fun SignoutScreen( } Scaffold( - modifier = Modifier.systemBarsPadding(), + modifier = Modifier.navigationBarsPadding(), topBar = { BtBackAppBar(title = stringResource(R.string.signout), onClickBack = navigateBack) }, bottomBar = { MainButton( From b76355d3d048435e1e5e991387b24088017768a1 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 01:00:58 +0900 Subject: [PATCH 55/90] =?UTF-8?q?Boolti-343=20style:=20=ED=8B=B0=EC=BC=93?= =?UTF-8?q?=20=EC=84=A0=ED=83=9D=20=EB=B0=94=ED=85=80=EC=8B=9C=ED=8A=B8=20?= =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=ED=95=98=EB=8B=A8=20?= =?UTF-8?q?=ED=8C=A8=EB=94=A9=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../screen/ticketing/ChooseTicketBottomSheet.kt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/ChooseTicketBottomSheet.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/ChooseTicketBottomSheet.kt index d3cff8573..5d452aeef 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/ChooseTicketBottomSheet.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/ChooseTicketBottomSheet.kt @@ -6,12 +6,9 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -104,11 +101,7 @@ fun ChooseTicketBottomSheet( ) { Column( modifier = Modifier - .padding( - bottom = 20.dp + WindowInsets.navigationBars - .asPaddingValues() - .calculateBottomPadding() - ) + .padding(bottom = 20.dp) .heightIn(max = 564.dp) ) { uiState.selected?.let { From 849f31be4ad5b2d56372617f83524a21667f01c3 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 02:00:11 +0900 Subject: [PATCH 56/90] =?UTF-8?q?Boolti-343=20fix:=20=EB=A8=B8=EC=A7=80=20?= =?UTF-8?q?=EC=B6=A9=EB=8F=8C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/nexters/boolti/presentation/screen/my/MyScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt index 8592dfd0e..4aa616cc4 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt @@ -1,5 +1,6 @@ package com.nexters.boolti.presentation.screen.my +import android.widget.Toast import androidx.annotation.DrawableRes import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -63,7 +64,6 @@ fun MyScreen( val user by viewModel.user.collectAsStateWithLifecycle() val domain = BuildConfig.DOMAIN - val registrationUrl = "https://${domain}/show/add" val homeUrl = "https://${domain}/home" val uriHandler = LocalUriHandler.current val context = LocalContext.current From 32f45787a45ed96eedead97a15506290e549dee3 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 15:09:26 +0900 Subject: [PATCH 57/90] =?UTF-8?q?Boolti-343=20style:=20=ED=99=88=20?= =?UTF-8?q?=EC=83=81=EB=8B=A8=20=ED=8C=A8=EB=94=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/show/ShowScreen.kt | 3 ++- .../com/nexters/boolti/presentation/theme/Dimen.kt | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt index 5d45a70e9..c1a3f783e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt @@ -70,6 +70,7 @@ import com.nexters.boolti.presentation.theme.Grey85 import com.nexters.boolti.presentation.theme.marginHorizontal import com.nexters.boolti.presentation.theme.point1 import com.nexters.boolti.presentation.theme.point4 +import com.nexters.boolti.presentation.theme.statusBarHeight @OptIn(ExperimentalLayoutApi::class) @Composable @@ -128,7 +129,7 @@ fun ShowScreen( columns = GridCells.Adaptive(minSize = 150.dp), horizontalArrangement = Arrangement.spacedBy(15.dp), verticalArrangement = Arrangement.spacedBy(28.dp), - contentPadding = PaddingValues(top = 12.dp + appbarHeight), + contentPadding = PaddingValues(top = statusBarHeight + appbarHeight + 12.dp), ) { items( count = uiState.shows.size.coerceAtMost(4), diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/theme/Dimen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/theme/Dimen.kt index 34833bca3..800747caf 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/theme/Dimen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/theme/Dimen.kt @@ -1,7 +1,20 @@ package com.nexters.boolti.presentation.theme +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues +import androidx.compose.foundation.layout.statusBars +import androidx.compose.runtime.Composable import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp val marginHorizontal: Dp = 20.dp val subTextPadding: Dp = 6.dp + +val statusBarPaddingValues: PaddingValues + @Composable + get() = WindowInsets.statusBars.asPaddingValues() + +val statusBarHeight: Dp + @Composable + get() = statusBarPaddingValues.calculateTopPadding() From 567416d7ebde2e5b7125f6187c86272e5a1274dc Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 16:18:25 +0900 Subject: [PATCH 58/90] =?UTF-8?q?refactor/253=20develop=20=EB=A8=B8?= =?UTF-8?q?=EC=A7=80=20=EC=B6=A9=EB=8F=8C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/nexters/boolti/presentation/screen/Main.kt | 6 ++++-- .../boolti/presentation/screen/home/HomeScreen.kt | 5 +---- .../boolti/presentation/screen/my/MyNavigation.kt | 4 +++- .../screen/perforemdshows/PerformedShowsNavigation.kt | 11 ++++++----- .../presentation/screen/profile/ProfileNavigation.kt | 2 +- .../boolti/presentation/screen/qr/QrFullScreen.kt | 6 ++---- .../boolti/presentation/screen/show/ShowNavigation.kt | 4 +++- 7 files changed, 20 insertions(+), 18 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 0def3019a..9a6a2ccc1 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -33,6 +33,7 @@ import com.nexters.boolti.presentation.screen.login.loginScreen import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.navigation.ShowRoute import com.nexters.boolti.presentation.screen.payment.paymentCompleteScreen +import com.nexters.boolti.presentation.screen.perforemdshows.performedShowsScreen import com.nexters.boolti.presentation.screen.profile.profileScreen import com.nexters.boolti.presentation.screen.profileedit.link.profileLinkEditScreen import com.nexters.boolti.presentation.screen.profileedit.profile.profileEditScreen @@ -44,10 +45,10 @@ import com.nexters.boolti.presentation.screen.reservations.reservationsScreen import com.nexters.boolti.presentation.screen.showdetail.showDetailContentScreen import com.nexters.boolti.presentation.screen.showdetail.showDetailScreen import com.nexters.boolti.presentation.screen.showdetail.showImagesScreen +import com.nexters.boolti.presentation.screen.showregistration.addShowRegistration import com.nexters.boolti.presentation.screen.signout.signoutScreen import com.nexters.boolti.presentation.screen.ticket.detail.ticketDetailScreen import com.nexters.boolti.presentation.screen.ticketing.ticketingScreen -import com.nexters.boolti.presentation.screen.showregistration.addShowRegistration import com.nexters.boolti.presentation.theme.BooltiTheme import com.nexters.boolti.presentation.util.SnackbarController import com.nexters.boolti.presentation.util.rememberNavControllerWithLog @@ -160,7 +161,6 @@ fun MainNavigation( ) qrFullScreen( navController = navController, - popBackStack = navController::popBackStack, getSharedViewModel = { entry -> entry.sharedViewModel(navController) }, ) } @@ -217,6 +217,8 @@ fun MainNavigation( ) } + performedShowsScreen(navController = navController) + addShowRegistration( modifier = modifier, popBackStack = navController::popBackStack, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt index 7e58b03b3..c68fa82b2 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt @@ -2,8 +2,6 @@ package com.nexters.boolti.presentation.screen.home import android.net.Uri import android.widget.Toast -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -24,7 +22,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -35,7 +32,6 @@ import androidx.navigation.NavDestination.Companion.hasRoute import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.compose.NavHost import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.navDeepLink import com.nexters.boolti.presentation.BuildConfig import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.extension.requireActivity @@ -148,6 +144,7 @@ fun HomeScreen( navigateToAccountSetting = navigateToAccountSetting, navigateToReservations = navigateToReservations, navigateToProfile = navigateToProfile, + navigateToShowRegistration = navigateToShowRegistration, navigateToQrScan = navigateToQrScan, ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt index 9a74cc987..b04e1bf8c 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt @@ -9,6 +9,7 @@ fun NavGraphBuilder.myScreen( navigateToAccountSetting: () -> Unit, navigateToReservations: () -> Unit, navigateToProfile: () -> Unit, + navigateToShowRegistration: () -> Unit, navigateToQrScan: () -> Unit, ) { composable { @@ -17,7 +18,8 @@ fun NavGraphBuilder.myScreen( onClickAccountSetting = navigateToAccountSetting, navigateToReservations = navigateToReservations, navigateToProfile = navigateToProfile, + navigateToShowRegistration = navigateToShowRegistration, onClickQrScan = navigateToQrScan, ) } -} \ No newline at end of file +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt index 386e96507..e41d50be1 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt @@ -1,13 +1,14 @@ package com.nexters.boolti.presentation.screen.perforemdshows import androidx.compose.ui.Modifier +import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute -fun NavGraphBuilder.PerformedShowsScreen( - navigateTo: (String) -> Unit, - popBackStack: () -> Unit, +fun NavGraphBuilder.performedShowsScreen( + navController: NavController, modifier: Modifier = Modifier, ) { composable( @@ -17,9 +18,9 @@ fun NavGraphBuilder.PerformedShowsScreen( PerformedShowsScreen( modifier = modifier, onClickShow = { show -> - navigateTo(MainDestination.ShowDetail.createRoute(show.id)) + navController.navigate(MainRoute.ShowDetail(showId = show.id)) }, - onClickBack = popBackStack, + onClickBack = navController::popBackStack, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt index aeb5c59ad..4caa8103d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt @@ -30,7 +30,7 @@ fun NavGraphBuilder.profileScreen( } }, navigateToProfileEdit = { navController.navigate(MainDestination.ProfileEdit.route) }, - navigateToShow = { showId -> navController.navigate(MainDestination.ShowDetail.createRoute(showId)) }, + navigateToShow = { showId -> navController.navigate(MainRoute.ShowDetail(showId = showId)) }, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt index a0150a688..ed6927d3c 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt @@ -1,6 +1,5 @@ package com.nexters.boolti.presentation.screen.qr -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -57,7 +56,6 @@ import com.nexters.boolti.presentation.util.rememberQrBitmapPainter fun NavGraphBuilder.qrFullScreen( navController: NavHostController, - popBackStack: () -> Unit, getSharedViewModel: @Composable (NavBackStackEntry) -> TicketDetailViewModel, modifier: Modifier = Modifier, ) { @@ -67,11 +65,11 @@ fun NavGraphBuilder.qrFullScreen( QrFullScreen( modifier = modifier, viewModel = getSharedViewModel(entry), - ) { popBackStack() } + onClose = navController::popBackStack, + ) } } -@OptIn(ExperimentalFoundationApi::class) @Composable fun QrFullScreen( modifier: Modifier = Modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt index eb682204c..69e6db026 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt @@ -9,6 +9,7 @@ import com.nexters.boolti.presentation.screen.navigation.HomeRoute fun NavGraphBuilder.showScreen( navigateToShowDetail: (showId: String) -> Unit, navigateToBusiness: () -> Unit, + navigateToShowRegistration: () -> Unit, ) { composable( deepLinks = listOf( @@ -21,6 +22,7 @@ fun NavGraphBuilder.showScreen( ShowScreen( onClickShowItem = navigateToShowDetail, navigateToBusiness = navigateToBusiness, + navigateToShowRegistration = navigateToShowRegistration, ) } -} \ No newline at end of file +} From 4f9ecb9ad3ae10ce746732c00c54eb10e37bbf74 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 18:17:03 +0900 Subject: [PATCH 59/90] =?UTF-8?q?refactor/253=20qr=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/nexters/boolti/presentation/screen/Main.kt | 1 - .../nexters/boolti/presentation/screen/MainDestination.kt | 2 -- .../nexters/boolti/presentation/screen/qr/QrFullScreen.kt | 6 ++---- .../presentation/screen/ticket/detail/TicketDetailScreen.kt | 6 +----- 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 9a6a2ccc1..af5b4534d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -155,7 +155,6 @@ fun MainNavigation( ) { ticketDetailScreen( navController = navController, - navigateTo = navController::navigateTo, popBackStack = navController::popBackStack, getSharedViewModel = { entry -> entry.sharedViewModel(navController) }, ) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index d1fd20754..ec2c2a288 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -11,8 +11,6 @@ sealed class MainDestination(val route: String) { val arguments = listOf(navArgument(ticketId) { type = NavType.StringType }) } - data object Qr : MainDestination(route = "qr") - data object ProfileEdit : MainDestination(route = "profileEdit") data object ProfileSnsEdit : MainDestination(route = "profileSnsEdit?sns={$snsType}&id={$linkId}&title={$linkTitle}&username={$username}") { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt index ed6927d3c..1d39b05ea 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt @@ -44,7 +44,7 @@ import androidx.navigation.compose.composable import com.nexters.boolti.domain.model.TicketState import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.component.InstagramIndicator -import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.ticket.detail.TicketDetailViewModel import com.nexters.boolti.presentation.theme.Grey10 import com.nexters.boolti.presentation.theme.Grey20 @@ -59,9 +59,7 @@ fun NavGraphBuilder.qrFullScreen( getSharedViewModel: @Composable (NavBackStackEntry) -> TicketDetailViewModel, modifier: Modifier = Modifier, ) { - composable( - route = MainDestination.Qr.route, - ) { entry -> + composable { entry -> QrFullScreen( modifier = modifier, viewModel = getSharedViewModel(entry), diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt index 0f0135bc5..de6c18b2c 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt @@ -96,7 +96,6 @@ import com.nexters.boolti.presentation.component.ShowInquiry import com.nexters.boolti.presentation.extension.toDp import com.nexters.boolti.presentation.extension.toPx import com.nexters.boolti.presentation.screen.LocalSnackbarController -import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.qr.QrCoverView import com.nexters.boolti.presentation.theme.BooltiTheme @@ -116,7 +115,6 @@ import com.nexters.boolti.presentation.util.rememberQrBitmapPainter fun NavGraphBuilder.ticketDetailScreen( navController: NavHostController, - navigateTo: (String) -> Unit, popBackStack: () -> Unit, getSharedViewModel: @Composable (NavBackStackEntry) -> TicketDetailViewModel, modifier: Modifier = Modifier, @@ -127,9 +125,7 @@ fun NavGraphBuilder.ticketDetailScreen( TicketDetailScreen( modifier = modifier, onBackClicked = popBackStack, - onClickQr = { - navigateTo(MainDestination.Qr.route) - }, + onClickQr = { navController.navigate(MainRoute.Qr) }, navigateToShowDetail = { navController.navigate(MainRoute.ShowDetail(showId = it)) }, viewModel = getSharedViewModel(entry), ) From 56310b6e81c6a0660c10a86c2c17d8b4dbfe94d6 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 18:26:23 +0900 Subject: [PATCH 60/90] =?UTF-8?q?refactor/253=20=EC=B0=B8=EC=97=AC?= =?UTF-8?q?=ED=95=9C=20=EA=B3=B5=EC=97=B0=20=EB=A7=88=EC=9D=B4=EA=B7=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/MainDestination.kt | 12 ------------ .../presentation/screen/navigation/MainRoute.kt | 5 +++++ .../perforemdshows/PerformedShowsNavigation.kt | 6 +----- .../presentation/screen/profile/ProfileNavigation.kt | 4 ++-- 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index ec2c2a288..b0a4189aa 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -68,18 +68,6 @@ sealed class MainDestination(val route: String) { fun createRoute(userCode: String) = "linkList?userCode=$userCode" } - data object PerformedShows : MainDestination(route = "performedShows?userCode={$userCode}") { - val arguments = listOf( - navArgument(userCode) { - type = NavType.StringType - nullable = true - }, - ) - - fun createRoute(): String = "performedShows" - fun createRoute(userCode: String) = "performedShows?userCode=$userCode" - } - data object ShowRegistration : MainDestination(route = "webView") } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt index d7e18f18b..604baa0ea 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt @@ -89,4 +89,9 @@ sealed interface MainRoute { val linkTitle: String?, val url: String? ) : MainRoute + + @Serializable + data class PerformedShows( + val userCode: String? = null, + ) : MainRoute } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt index e41d50be1..a46320f67 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt @@ -4,17 +4,13 @@ import androidx.compose.ui.Modifier import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable -import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.performedShowsScreen( navController: NavController, modifier: Modifier = Modifier, ) { - composable( - route = MainDestination.PerformedShows.route, - arguments = MainDestination.PerformedShows.arguments, - ) { + composable { PerformedShowsScreen( modifier = modifier, onClickShow = { show -> diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt index 4caa8103d..2ee82e4eb 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt @@ -24,9 +24,9 @@ fun NavGraphBuilder.profileScreen( }, navigateToPerformedShows = { userCode -> if (userCode != null) { - navController.navigate(MainDestination.PerformedShows.createRoute(userCode)) + navController.navigate(MainRoute.PerformedShows(userCode)) } else { - navController.navigate(MainDestination.PerformedShows.createRoute()) + navController.navigate(MainRoute.PerformedShows()) } }, navigateToProfileEdit = { navController.navigate(MainDestination.ProfileEdit.route) }, From 83885dbcdcea7c836d25a2356364c2b042c086f1 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 18:49:07 +0900 Subject: [PATCH 61/90] =?UTF-8?q?refactor/253=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EB=B0=8F=20toRoute=20=EB=A1=9C=20?= =?UTF-8?q?=EB=8C=80=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nexters/boolti/presentation/screen/Main.kt | 2 ++ .../boolti/presentation/screen/MainDestination.kt | 15 +-------------- .../screen/link/LinkListNavigation.kt | 14 ++++++-------- .../presentation/screen/link/LinkListScreen.kt | 4 +++- .../presentation/screen/link/LinkListViewModel.kt | 8 ++++++-- .../presentation/screen/navigation/MainRoute.kt | 5 +++++ .../screen/perforemdshows/PerformedShowsScreen.kt | 6 ++++-- .../perforemdshows/PerformedShowsViewModel.kt | 8 ++++++-- .../screen/profile/ProfileNavigation.kt | 12 ++---------- .../screen/profile/ProfileViewModel.kt | 5 +++-- 10 files changed, 38 insertions(+), 41 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index af5b4534d..3bb5edb4d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -29,6 +29,7 @@ import com.nexters.boolti.presentation.screen.business.businessScreen import com.nexters.boolti.presentation.screen.gift.giftScreen import com.nexters.boolti.presentation.screen.giftcomplete.giftCompleteScreen import com.nexters.boolti.presentation.screen.home.homeScreen +import com.nexters.boolti.presentation.screen.link.linkListScreen import com.nexters.boolti.presentation.screen.login.loginScreen import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.navigation.ShowRoute @@ -216,6 +217,7 @@ fun MainNavigation( ) } + linkListScreen(navController = navController) performedShowsScreen(navController = navController) addShowRegistration( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index b0a4189aa..6fe769250 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -56,18 +56,6 @@ sealed class MainDestination(val route: String) { "profileLinkEdit?id=${link.id}&title=${link.name}&url=${link.url}" } - data object LinkList : MainDestination(route = "linkList?userCode={$userCode}") { - val arguments = listOf( - navArgument(userCode) { - type = NavType.StringType - nullable = true - }, - ) - - fun createRoute(): String = "linkList" - fun createRoute(userCode: String) = "linkList?userCode=$userCode" - } - data object ShowRegistration : MainDestination(route = "webView") } @@ -77,9 +65,8 @@ sealed class MainDestination(val route: String) { const val showId = "showId" const val ticketId = "ticketId" const val ticketName = "ticketName" -const val userCode = "userCode" const val linkId = "linkId" const val linkTitle = "linkTitle" const val url = "url" const val snsType = "snsType" -const val username = "username" \ No newline at end of file +const val username = "username" diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListNavigation.kt index bd99fd252..09593f1c0 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListNavigation.kt @@ -1,21 +1,19 @@ package com.nexters.boolti.presentation.screen.link import androidx.compose.ui.Modifier +import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable -import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.MainRoute -fun NavGraphBuilder.LinkListScreen( +fun NavGraphBuilder.linkListScreen( + navController: NavController, modifier: Modifier = Modifier, - popBackStack: () -> Unit, ) { - composable( - route = MainDestination.LinkList.route, - arguments = MainDestination.LinkList.arguments, - ) { + composable { LinkListScreen( modifier = modifier, - onClickBack = popBackStack, + onClickBack = navController::popBackStack, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListScreen.kt index b9d6683fb..e21508a05 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListScreen.kt @@ -102,7 +102,9 @@ private fun LinkListScreen( } if (loading) { BtCircularProgressIndicator( - modifier = Modifier.align(Alignment.Center) + modifier = Modifier + .fillMaxSize() + .align(Alignment.Center), ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListViewModel.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListViewModel.kt index 1f078b43b..6e720811e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListViewModel.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListViewModel.kt @@ -2,10 +2,11 @@ package com.nexters.boolti.presentation.screen.link import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope +import androidx.navigation.toRoute import com.nexters.boolti.domain.repository.MemberRepository import com.nexters.boolti.domain.usecase.GetUserUsecase import com.nexters.boolti.presentation.base.BaseViewModel -import com.nexters.boolti.presentation.screen.userCode +import com.nexters.boolti.presentation.screen.navigation.MainRoute import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow @@ -32,7 +33,7 @@ class LinkListViewModel @Inject constructor( } private fun initLoad() { - savedStateHandle.get(userCode)?.let { userCode -> + savedStateHandle.toRoute().userCode?.let { userCode -> fetchOthersLinks(userCode) } ?: run { fetchMyLinks() @@ -49,6 +50,9 @@ class LinkListViewModel @Inject constructor( ) } }.onFailure { + _uiState.update { + it.copy(loading = false) + } event(LinkListEvent.LoadFailed) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt index 604baa0ea..c9a278838 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt @@ -90,6 +90,11 @@ sealed interface MainRoute { val url: String? ) : MainRoute + @Serializable + data class LinkList( + val userCode: String? = null, + ) : MainRoute + @Serializable data class PerformedShows( val userCode: String? = null, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsScreen.kt index cdf0aad4d..a232e174d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsScreen.kt @@ -70,7 +70,7 @@ private fun PerformedShowsScreen( topBar = { BtBackAppBar( title = stringResource(R.string.performed_shows), - onClickBack = onClickBack + onClickBack = onClickBack, ) }, ) { innerPadding -> @@ -93,7 +93,9 @@ private fun PerformedShowsScreen( if (loading) { BtCircularProgressIndicator( - modifier = Modifier.align(Alignment.Center), + modifier = Modifier + .fillMaxSize() + .align(Alignment.Center), ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsViewModel.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsViewModel.kt index 2fdc6c055..f22bb8a13 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsViewModel.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsViewModel.kt @@ -2,10 +2,11 @@ package com.nexters.boolti.presentation.screen.perforemdshows import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope +import androidx.navigation.toRoute import com.nexters.boolti.domain.repository.MemberRepository import com.nexters.boolti.domain.usecase.GetUserUsecase import com.nexters.boolti.presentation.base.BaseViewModel -import com.nexters.boolti.presentation.screen.userCode +import com.nexters.boolti.presentation.screen.navigation.MainRoute import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow @@ -32,7 +33,7 @@ class PerformedShowsViewModel @Inject constructor( } private fun initLoad() { - savedStateHandle.get(userCode)?.let { userCode -> + savedStateHandle.toRoute().userCode?.let { userCode -> fetchOthersShows(userCode) } ?: fetchMyShows() } @@ -62,6 +63,9 @@ class PerformedShowsViewModel @Inject constructor( } } .onFailure { + _uiState.update { + it.copy(loading = false) + } event(PerformedShowsEvent.FetchFailed) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt index 2ee82e4eb..f651b7a50 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt @@ -16,18 +16,10 @@ fun NavGraphBuilder.profileScreen( modifier = modifier, onClickBack = navController::popBackStack, navigateToLinks = { userCode -> - if (userCode != null) { - navController.navigate(MainDestination.LinkList.createRoute(userCode)) - } else { - navController.navigate(MainDestination.LinkList.createRoute()) - } + navController.navigate(MainRoute.LinkList(userCode)) }, navigateToPerformedShows = { userCode -> - if (userCode != null) { - navController.navigate(MainRoute.PerformedShows(userCode)) - } else { - navController.navigate(MainRoute.PerformedShows()) - } + navController.navigate(MainRoute.PerformedShows(userCode)) }, navigateToProfileEdit = { navController.navigate(MainDestination.ProfileEdit.route) }, navigateToShow = { showId -> navController.navigate(MainRoute.ShowDetail(showId = showId)) }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileViewModel.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileViewModel.kt index 5932ef87f..74dd7f0e2 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileViewModel.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileViewModel.kt @@ -2,12 +2,13 @@ package com.nexters.boolti.presentation.screen.profile import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope +import androidx.navigation.toRoute import com.nexters.boolti.domain.model.User import com.nexters.boolti.domain.repository.AuthRepository import com.nexters.boolti.domain.repository.MemberRepository import com.nexters.boolti.domain.usecase.GetUserUsecase import com.nexters.boolti.presentation.base.BaseViewModel -import com.nexters.boolti.presentation.screen.userCode +import com.nexters.boolti.presentation.screen.navigation.MainRoute import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow @@ -27,7 +28,7 @@ class ProfileViewModel @Inject constructor( private val memberRepository: MemberRepository, private val authRepository: AuthRepository, ) : BaseViewModel() { - private val _userCode: String? = savedStateHandle[userCode] + private val _userCode: String? = savedStateHandle.toRoute().userCode private val myProfile: User.My = getUserUsecase() ?: User.My("-999") private val isMyProfile = _userCode?.equals(myProfile.userCode) ?: true From 4c3f9fcf9000bc2f2786d65dcfa13e22bb7238f5 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 19:29:26 +0900 Subject: [PATCH 62/90] =?UTF-8?q?refactor/253=20SNS=20=ED=8E=B8=EC=A7=91?= =?UTF-8?q?=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EB=B0=8F=20=ED=81=AC=EB=9E=98=EC=8B=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/nexters/boolti/domain/model/Sns.kt | 3 ++ .../boolti/presentation/screen/Main.kt | 15 +++--- .../presentation/screen/MainDestination.kt | 25 ---------- .../screen/navigation/ProfileRoute.kt | 25 ++++++++++ .../screen/profile/ProfileNavigation.kt | 4 +- .../link/ProfileLinkEditNavigation.kt | 3 +- .../profile/ProfileEditNavigation.kt | 29 ++++------- .../sns/ProfileSnsEditNavigation.kt | 48 +++++++++++++------ .../profileedit/sns/SnsEditViewModel.kt | 13 +++-- 9 files changed, 86 insertions(+), 79 deletions(-) create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ProfileRoute.kt diff --git a/domain/src/main/java/com/nexters/boolti/domain/model/Sns.kt b/domain/src/main/java/com/nexters/boolti/domain/model/Sns.kt index fe9c4583c..7b969a0b1 100644 --- a/domain/src/main/java/com/nexters/boolti/domain/model/Sns.kt +++ b/domain/src/main/java/com/nexters/boolti/domain/model/Sns.kt @@ -1,10 +1,13 @@ package com.nexters.boolti.domain.model +import kotlinx.serialization.Serializable + data class Sns( val id: String, val type: SnsType, val username: String, ) { + @Serializable enum class SnsType { INSTAGRAM, YOUTUBE; diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 3bb5edb4d..e75527177 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -32,12 +32,14 @@ import com.nexters.boolti.presentation.screen.home.homeScreen import com.nexters.boolti.presentation.screen.link.linkListScreen import com.nexters.boolti.presentation.screen.login.loginScreen import com.nexters.boolti.presentation.screen.navigation.MainRoute +import com.nexters.boolti.presentation.screen.navigation.ProfileRoute import com.nexters.boolti.presentation.screen.navigation.ShowRoute import com.nexters.boolti.presentation.screen.payment.paymentCompleteScreen import com.nexters.boolti.presentation.screen.perforemdshows.performedShowsScreen import com.nexters.boolti.presentation.screen.profile.profileScreen import com.nexters.boolti.presentation.screen.profileedit.link.profileLinkEditScreen import com.nexters.boolti.presentation.screen.profileedit.profile.profileEditScreen +import com.nexters.boolti.presentation.screen.profileedit.sns.profileSnsEditScreen import com.nexters.boolti.presentation.screen.qr.hostedShowScreen import com.nexters.boolti.presentation.screen.qr.qrFullScreen import com.nexters.boolti.presentation.screen.refund.refundScreen @@ -177,15 +179,11 @@ fun MainNavigation( businessScreen(navController = navController) accountSettingScreen(navController = navController) profileScreen(navController = navController) - navigation( - route = "profileEditNavigation", - startDestination = MainDestination.ProfileEdit.route, + navigation( + startDestination = ProfileRoute.ProfileEdit, ) { - profileEditScreen( - navController = navController, - navigateTo = navController::navigate, - popBackStack = navController::popBackStack, - ) + profileEditScreen(navController = navController) + profileSnsEditScreen(navController = navController) profileLinkEditScreen( navController = navController, onAddLink = { linkName, url -> @@ -213,7 +211,6 @@ fun MainNavigation( ?.set("removeLinkId", id) navController.popBackStack() }, - popBackStack = navController::popBackStack, ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index 6fe769250..f625254f9 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -3,7 +3,6 @@ package com.nexters.boolti.presentation.screen import androidx.navigation.NavType import androidx.navigation.navArgument import com.nexters.boolti.domain.model.Link -import com.nexters.boolti.domain.model.Sns sealed class MainDestination(val route: String) { @@ -11,29 +10,6 @@ sealed class MainDestination(val route: String) { val arguments = listOf(navArgument(ticketId) { type = NavType.StringType }) } - data object ProfileEdit : MainDestination(route = "profileEdit") - data object ProfileSnsEdit : - MainDestination(route = "profileSnsEdit?sns={$snsType}&id={$linkId}&title={$linkTitle}&username={$username}") { - val arguments = listOf( - navArgument(snsType) { - type = NavType.StringType - nullable = true - }, - navArgument(linkId) { - type = NavType.StringType - nullable = true - }, - navArgument(username) { - type = NavType.StringType - nullable = true - }, - ) - - fun createRoute(): String = "profileSnsEdit" - fun createRoute(sns: Sns): String = - "profileSnsEdit?sns=${sns.type}&id=${sns.id}&username=${sns.username}" - } - data object ProfileLinkEdit : MainDestination(route = "profileLinkEdit?id={$linkId}&title={$linkTitle}&url={$url}") { val arguments = listOf( @@ -68,5 +44,4 @@ const val ticketName = "ticketName" const val linkId = "linkId" const val linkTitle = "linkTitle" const val url = "url" -const val snsType = "snsType" const val username = "username" diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ProfileRoute.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ProfileRoute.kt new file mode 100644 index 000000000..87a4f960d --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ProfileRoute.kt @@ -0,0 +1,25 @@ +package com.nexters.boolti.presentation.screen.navigation + +import com.nexters.boolti.domain.model.Sns +import kotlinx.serialization.Serializable + +sealed interface ProfileRoute { + @Serializable + data object ProfileRoot : ProfileRoute + + @Serializable + data object ProfileEdit : ProfileRoute + + @Serializable + data class ProfileSnsEdit( + val snsType: Sns.SnsType = Sns.SnsType.INSTAGRAM, + val linkId: String? = null, + val username: String? = null, + ) : ProfileRoute { + constructor(sns: Sns?) : this( + snsType = sns?.type ?: Sns.SnsType.INSTAGRAM, + linkId = sns?.id, + username = sns?.username, + ) + } +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt index f651b7a50..b860a2c87 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt @@ -4,8 +4,8 @@ import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable -import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute +import com.nexters.boolti.presentation.screen.navigation.ProfileRoute fun NavGraphBuilder.profileScreen( navController: NavHostController, @@ -21,7 +21,7 @@ fun NavGraphBuilder.profileScreen( navigateToPerformedShows = { userCode -> navController.navigate(MainRoute.PerformedShows(userCode)) }, - navigateToProfileEdit = { navController.navigate(MainDestination.ProfileEdit.route) }, + navigateToProfileEdit = { navController.navigate(ProfileRoute.ProfileEdit) }, navigateToShow = { showId -> navController.navigate(MainRoute.ShowDetail(showId = showId)) }, ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt index 28c6fe8b2..d4cbcb1a0 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt @@ -11,7 +11,6 @@ fun NavGraphBuilder.profileLinkEditScreen( onAddLink: (linkName: String, url: String) -> Unit, onEditLink: (id: String, linkName: String, url: String) -> Unit, onRemoveLink: (id: String) -> Unit, - popBackStack: () -> Unit, modifier: Modifier = Modifier, ) { composable( @@ -23,7 +22,7 @@ fun NavGraphBuilder.profileLinkEditScreen( onAddLink = onAddLink, onEditLink = onEditLink, onRemoveLink = onRemoveLink, - navigateBack = popBackStack, + navigateBack = navController::popBackStack, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt index f229bc8dc..a70119014 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt @@ -6,21 +6,18 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.domain.model.Link import com.nexters.boolti.domain.model.Sns -import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.ProfileRoute import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.zip import java.util.UUID fun NavGraphBuilder.profileEditScreen( navController: NavHostController, - navigateTo: (String) -> Unit, - popBackStack: () -> Unit, modifier: Modifier = Modifier, ) { - composable(route = MainDestination.ProfileEdit.route) { backStackEntry -> + composable { backStackEntry -> // 새 링크 추가 val newLinkName = backStackEntry.savedStateHandle .getStateFlow("newLinkName", null).filterNotNull() @@ -66,8 +63,7 @@ fun NavGraphBuilder.profileEditScreen( // 새 SNS 추가 val newSnsType = backStackEntry.savedStateHandle - .getStateFlow("newSnsType", null) - .map(Sns.SnsType::fromString) + .getStateFlow("newSnsType", null) .filterNotNull() val newSnsUsername = backStackEntry.savedStateHandle @@ -80,7 +76,7 @@ fun NavGraphBuilder.profileEditScreen( username = username, ) }.onEach { - backStackEntry.savedStateHandle.remove("newSnsType") + backStackEntry.savedStateHandle.remove("newSnsType") backStackEntry.savedStateHandle.remove("newSnsUsername") } @@ -88,8 +84,7 @@ fun NavGraphBuilder.profileEditScreen( val editSnsId = backStackEntry.savedStateHandle .getStateFlow("editSnsId", null).filterNotNull() val editSnsType = backStackEntry.savedStateHandle - .getStateFlow("editSnsType", null) - .map(Sns.SnsType::fromString) + .getStateFlow("editSnsType", null) .filterNotNull() val editSnsUsername = backStackEntry.savedStateHandle .getStateFlow("editSnsUsername", null).filterNotNull() @@ -101,7 +96,7 @@ fun NavGraphBuilder.profileEditScreen( Sns(id = id, type = type, username = username) }.onEach { backStackEntry.savedStateHandle.remove("editSnsId") - backStackEntry.savedStateHandle.remove("editSnsType") + backStackEntry.savedStateHandle.remove("editSnsType") backStackEntry.savedStateHandle.remove("editSnsUsername") } @@ -114,20 +109,16 @@ fun NavGraphBuilder.profileEditScreen( ProfileEditScreen( modifier = modifier, - navigateBack = popBackStack, + navigateBack = navController::popBackStack, navigateToSnsEdit = { sns -> - sns?.let { - navigateTo(MainDestination.ProfileSnsEdit.createRoute(it)) - } ?: run { - navigateTo(MainDestination.ProfileSnsEdit.createRoute()) - } + navController.navigate(ProfileRoute.ProfileSnsEdit(sns)) }, navigateToLinkEdit = { link -> - link?.let { + /*link?.let { navigateTo(MainDestination.ProfileLinkEdit.createRoute(it)) } ?: run { navigateTo(MainDestination.ProfileLinkEdit.createRoute()) - } + }*/ }, newLinkCallback = newLink, editLinkCallback = editLink, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/ProfileSnsEditNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/ProfileSnsEditNavigation.kt index 276a9404a..401a258e9 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/ProfileSnsEditNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/ProfileSnsEditNavigation.kt @@ -1,28 +1,46 @@ package com.nexters.boolti.presentation.screen.profileedit.sns import androidx.compose.ui.Modifier +import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable -import com.nexters.boolti.domain.model.Sns -import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.ProfileRoute -fun NavGraphBuilder.ProfileSnsEditScreen( - onAddSns: (type: Sns.SnsType, username: String) -> Unit, - onEditSns: (id: String, type: Sns.SnsType, username: String) -> Unit, - onRemoveSns: (id: String) -> Unit, - popBackStack: () -> Unit, +fun NavGraphBuilder.profileSnsEditScreen( + navController: NavController, modifier: Modifier = Modifier, ) { - composable( - route = MainDestination.ProfileSnsEdit.route, - arguments = MainDestination.ProfileSnsEdit.arguments, - ) { + composable { SnsEditScreen( modifier = modifier, - onAddSns = onAddSns, - onEditSns = onEditSns, - onRemoveSns = onRemoveSns, - navigateBack = popBackStack, + onAddSns = { type, username -> + navController.previousBackStackEntry + ?.savedStateHandle + ?.apply { + set("newSnsType", type) + set("newSnsUsername", username) + } + navController.popBackStack() + }, + onEditSns = { id, type, username -> + navController.previousBackStackEntry + ?.savedStateHandle + ?.apply { + set("editSnsId", id) + set("editSnsType", type) + set("editSnsUsername", username) + } + navController.popBackStack() + }, + onRemoveSns = { id -> + navController.previousBackStackEntry + ?.savedStateHandle + ?.apply { + set("removeSnsId", id) + } + navController.popBackStack() + }, + navigateBack = navController::popBackStack, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/SnsEditViewModel.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/SnsEditViewModel.kt index 38f91db2d..98faf901b 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/SnsEditViewModel.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/SnsEditViewModel.kt @@ -1,11 +1,10 @@ package com.nexters.boolti.presentation.screen.profileedit.sns import androidx.lifecycle.SavedStateHandle +import androidx.navigation.toRoute import com.nexters.boolti.domain.model.Sns import com.nexters.boolti.presentation.base.BaseViewModel -import com.nexters.boolti.presentation.screen.linkId -import com.nexters.boolti.presentation.screen.snsType -import com.nexters.boolti.presentation.screen.username +import com.nexters.boolti.presentation.screen.navigation.ProfileRoute import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -16,12 +15,12 @@ import javax.inject.Inject class SnsEditViewModel @Inject constructor( savedStateHandle: SavedStateHandle, ) : BaseViewModel() { + private val route = savedStateHandle.toRoute() private val _uiState = MutableStateFlow( SnsEditState( - snsId = savedStateHandle[linkId], - selectedSns = Sns.SnsType.fromString(savedStateHandle[snsType]) - ?: Sns.SnsType.INSTAGRAM, - username = savedStateHandle[username] ?: "", + snsId = route.linkId, + selectedSns = route.snsType, + username = route.username ?: "", ) ) val uiState = _uiState.asStateFlow() From a2af5c8ba169566f2fa8dd8df1d461ba5f0debff Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 19:42:04 +0900 Subject: [PATCH 63/90] =?UTF-8?q?refactor/253=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=ED=8E=B8=EC=A7=91=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/Main.kt | 29 +------------- .../presentation/screen/MainDestination.kt | 28 -------------- .../screen/navigation/ProfileRoute.kt | 14 +++++++ .../profileedit/link/LinkEditViewModel.kt | 14 +++---- .../link/ProfileLinkEditNavigation.kt | 38 +++++++++++++------ .../profile/ProfileEditNavigation.kt | 6 +-- 6 files changed, 49 insertions(+), 80 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index e75527177..8fd74ee2d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -184,34 +184,7 @@ fun MainNavigation( ) { profileEditScreen(navController = navController) profileSnsEditScreen(navController = navController) - profileLinkEditScreen( - navController = navController, - onAddLink = { linkName, url -> - navController.previousBackStackEntry - ?.savedStateHandle - ?.apply { - set("newLinkName", linkName) - set("newLinkUrl", url) - } - navController.popBackStack() - }, - onEditLink = { id, linkName, url -> - navController.previousBackStackEntry - ?.savedStateHandle - ?.apply { - set("editLinkId", id) - set("editLinkName", linkName) - set("editLinkUrl", url) - } - navController.popBackStack() - }, - onRemoveLink = { id -> - navController.previousBackStackEntry - ?.savedStateHandle - ?.set("removeLinkId", id) - navController.popBackStack() - }, - ) + profileLinkEditScreen(navController = navController) } linkListScreen(navController = navController) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index f625254f9..6e24e77ab 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -2,7 +2,6 @@ package com.nexters.boolti.presentation.screen import androidx.navigation.NavType import androidx.navigation.navArgument -import com.nexters.boolti.domain.model.Link sealed class MainDestination(val route: String) { @@ -10,28 +9,6 @@ sealed class MainDestination(val route: String) { val arguments = listOf(navArgument(ticketId) { type = NavType.StringType }) } - data object ProfileLinkEdit : - MainDestination(route = "profileLinkEdit?id={$linkId}&title={$linkTitle}&url={$url}") { - val arguments = listOf( - navArgument(linkId) { - type = NavType.StringType - nullable = true - }, - navArgument(linkTitle) { - type = NavType.StringType - nullable = true - }, - navArgument(url) { - type = NavType.StringType - nullable = true - }, - ) - - fun createRoute(): String = "profileLinkEdit" - fun createRoute(link: Link): String = - "profileLinkEdit?id=${link.id}&title=${link.name}&url=${link.url}" - } - data object ShowRegistration : MainDestination(route = "webView") } @@ -40,8 +17,3 @@ sealed class MainDestination(val route: String) { */ const val showId = "showId" const val ticketId = "ticketId" -const val ticketName = "ticketName" -const val linkId = "linkId" -const val linkTitle = "linkTitle" -const val url = "url" -const val username = "username" diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ProfileRoute.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ProfileRoute.kt index 87a4f960d..710956f7a 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ProfileRoute.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ProfileRoute.kt @@ -1,5 +1,6 @@ package com.nexters.boolti.presentation.screen.navigation +import com.nexters.boolti.domain.model.Link import com.nexters.boolti.domain.model.Sns import kotlinx.serialization.Serializable @@ -22,4 +23,17 @@ sealed interface ProfileRoute { username = sns?.username, ) } + + @Serializable + data class ProfileLinkEdit( + val linkId: String? = null, + val linkTitle: String? = null, + val url: String? = null, + ) { + constructor(link: Link?) : this( + linkId = link?.id, + linkTitle = link?.name, + url = link?.url, + ) + } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/LinkEditViewModel.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/LinkEditViewModel.kt index b7327c177..04747d34e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/LinkEditViewModel.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/LinkEditViewModel.kt @@ -2,9 +2,8 @@ package com.nexters.boolti.presentation.screen.profileedit.link import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel -import com.nexters.boolti.presentation.screen.linkId -import com.nexters.boolti.presentation.screen.linkTitle -import com.nexters.boolti.presentation.screen.url +import androidx.navigation.toRoute +import com.nexters.boolti.presentation.screen.navigation.ProfileRoute import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -15,17 +14,16 @@ import javax.inject.Inject class LinkEditViewModel @Inject constructor( savedStateHandle: SavedStateHandle, ) : ViewModel() { - val editLinkId = savedStateHandle[linkId] ?: "" + private val route = savedStateHandle.toRoute() + val editLinkId = route.linkId ?: "" private val _uiState = MutableStateFlow( LinkEditState( - linkName = savedStateHandle[linkTitle] ?: "", - url = savedStateHandle[url] ?: "", + linkName = route.linkTitle ?: "", + url = route.url ?: "", ) ) val uiState = _uiState.asStateFlow() - fun complete() {} - fun onChangeLinkName(name: String) { _uiState.update { it.copy(linkName = name) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt index d4cbcb1a0..2b1652f7b 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt @@ -4,24 +4,40 @@ import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable -import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.ProfileRoute fun NavGraphBuilder.profileLinkEditScreen( navController: NavHostController, - onAddLink: (linkName: String, url: String) -> Unit, - onEditLink: (id: String, linkName: String, url: String) -> Unit, - onRemoveLink: (id: String) -> Unit, modifier: Modifier = Modifier, ) { - composable( - route = MainDestination.ProfileLinkEdit.route, - arguments = MainDestination.ProfileLinkEdit.arguments, - ) { + composable { LinkEditScreen( modifier = modifier, - onAddLink = onAddLink, - onEditLink = onEditLink, - onRemoveLink = onRemoveLink, + onAddLink = { linkName, url -> + navController.previousBackStackEntry + ?.savedStateHandle + ?.apply { + set("newLinkName", linkName) + set("newLinkUrl", url) + } + navController.popBackStack() + }, + onEditLink = { id, linkName, url -> + navController.previousBackStackEntry + ?.savedStateHandle + ?.apply { + set("editLinkId", id) + set("editLinkName", linkName) + set("editLinkUrl", url) + } + navController.popBackStack() + }, + onRemoveLink = { id -> + navController.previousBackStackEntry + ?.savedStateHandle + ?.set("removeLinkId", id) + navController.popBackStack() + }, navigateBack = navController::popBackStack, ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt index a70119014..8a89a272e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt @@ -114,11 +114,7 @@ fun NavGraphBuilder.profileEditScreen( navController.navigate(ProfileRoute.ProfileSnsEdit(sns)) }, navigateToLinkEdit = { link -> - /*link?.let { - navigateTo(MainDestination.ProfileLinkEdit.createRoute(it)) - } ?: run { - navigateTo(MainDestination.ProfileLinkEdit.createRoute()) - }*/ + navController.navigate(ProfileRoute.ProfileLinkEdit(link)) }, newLinkCallback = newLink, editLinkCallback = editLink, From 6c160ca26f5235490fdd316a8a2acdb8527023ee Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 20:15:43 +0900 Subject: [PATCH 64/90] =?UTF-8?q?refactor/253=20localProvider=20=EB=A1=9C?= =?UTF-8?q?=20NavController=20=EC=A0=9C=EA=B3=B5=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReservationDetailNavigation.kt | 7 +- .../boolti/presentation/screen/Main.kt | 85 ++++++++----------- .../AccountSettingNavigation.kt | 7 +- .../screen/business/BusinessNavigation.kt | 9 +- .../screen/gift/GiftNavigation.kt | 4 +- .../giftcomplete/GiftCompleteNavigation.kt | 7 +- .../screen/home/HomeNavigation.kt | 4 +- .../screen/link/LinkListNavigation.kt | 4 +- .../screen/login/LoginNavigation.kt | 4 +- .../screen/payment/PaymentNavigation.kt | 7 +- .../PerformedShowsNavigation.kt | 4 +- .../screen/profile/ProfileNavigation.kt | 4 +- .../link/ProfileLinkEditNavigation.kt | 4 +- .../profile/ProfileEditNavigation.kt | 5 +- .../sns/ProfileSnsEditNavigation.kt | 4 +- .../screen/qr/HostedShowNavigation.kt | 4 +- .../presentation/screen/qr/QrFullScreen.kt | 4 +- .../screen/refund/RefundNavigation.kt | 8 +- .../screen/report/ReportNavigation.kt | 11 ++- .../reservations/ReservationsNavigation.kt | 10 +-- .../showdetail/ShowDetailContentNavigation.kt | 7 +- .../screen/showdetail/ShowDetailNavigation.kt | 11 ++- .../screen/showdetail/ShowImagesNavigation.kt | 7 +- .../ShowRegistrationNavigation.kt | 5 +- .../screen/signout/SignoutNavigation.kt | 8 +- .../ticket/detail/TicketDetailScreen.kt | 7 +- .../screen/ticketing/TicketingNavigation.kt | 4 +- 27 files changed, 111 insertions(+), 134 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt index 9ae16b218..68cbba651 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailNavigation.kt @@ -1,14 +1,13 @@ package com.nexters.boolti.presentation.reservationdetail import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute -fun NavGraphBuilder.reservationDetailScreen( - navController: NavHostController, -) { +fun NavGraphBuilder.reservationDetailScreen() { composable { + val navController = LocalNavController.current ReservationDetailScreen( onBackPressed = navController::popBackStack, navigateToRefund = { id, isGift -> diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 8fd74ee2d..7113ce928 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -8,6 +8,7 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.staticCompositionLocalOf @@ -22,7 +23,6 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.navigation import androidx.navigation.navDeepLink import com.nexters.boolti.presentation.component.ToastSnackbarHost -import com.nexters.boolti.presentation.extension.navigateToHome import com.nexters.boolti.presentation.reservationdetail.reservationDetailScreen import com.nexters.boolti.presentation.screen.accountsetting.accountSettingScreen import com.nexters.boolti.presentation.screen.business.businessScreen @@ -60,11 +60,16 @@ val LocalSnackbarController = staticCompositionLocalOf { SnackbarController(SnackbarHostState()) } +val LocalNavController = compositionLocalOf { + error("No NavController provided") +} + @Composable fun Main(onClickQrScan: (showId: String, showName: String) -> Unit) { val modifier = Modifier.fillMaxSize() val scope = rememberCoroutineScope() val snackbarHostState = remember { SnackbarHostState() } + val rootNavController = rememberNavControllerWithLog() BooltiTheme { Surface(modifier) { @@ -79,8 +84,9 @@ fun Main(onClickQrScan: (showId: String, showName: String) -> Unit) { CompositionLocalProvider( LocalSnackbarController provides SnackbarController( snackbarHostState, - scope - ) + scope, + ), + LocalNavController provides rootNavController, ) { MainNavigation( modifier = modifier.padding(innerPadding), @@ -96,19 +102,20 @@ fun Main(onClickQrScan: (showId: String, showName: String) -> Unit) { fun MainNavigation( onClickQrScan: (showId: String, showName: String) -> Unit, modifier: Modifier = Modifier, - navController: NavHostController = rememberNavControllerWithLog(), ) { + val navController = LocalNavController.current + NavHost( modifier = modifier, navController = navController, startDestination = MainRoute.Home, ) { - homeScreen(navController = navController) - loginScreen(navController = navController) - signoutScreen(navController = navController) - reservationsScreen(navController = navController) - reservationDetailScreen(navController = navController) - refundScreen(navController = navController) + homeScreen() + loginScreen() + signoutScreen() + reservationsScreen() + reservationDetailScreen() + refundScreen() navigation( startDestination = ShowRoute.Detail, @@ -120,30 +127,19 @@ fun MainNavigation( ), ) { showDetailScreen( - navController = navController, navigateTo = navController::navigateTo, - popBackStack = navController::popBackStack, - navigateToHome = navController::navigateToHome, - getSharedViewModel = { entry -> entry.sharedViewModel(navController) } + getSharedViewModel = { entry -> entry.sharedViewModel() } ) showImagesScreen( - navController = navController, - popBackStack = navController::popBackStack, - getSharedViewModel = { entry -> entry.sharedViewModel(navController) } + getSharedViewModel = { entry -> entry.sharedViewModel() } ) showDetailContentScreen( - navController = navController, - popBackStack = navController::popBackStack, - getSharedViewModel = { entry -> entry.sharedViewModel(navController) } - ) - reportScreen( - navController = navController, - navigateToHome = navController::navigateToHome, - popBackStack = navController::popBackStack, + getSharedViewModel = { entry -> entry.sharedViewModel() } ) + reportScreen() } - ticketingScreen(navController = navController) + ticketingScreen() navigation( route = "${MainDestination.TicketDetail.route}/{$ticketId}", @@ -157,49 +153,42 @@ fun MainNavigation( arguments = MainDestination.TicketDetail.arguments, ) { ticketDetailScreen( - navController = navController, - popBackStack = navController::popBackStack, - getSharedViewModel = { entry -> entry.sharedViewModel(navController) }, + getSharedViewModel = { entry -> entry.sharedViewModel() }, ) qrFullScreen( - navController = navController, - getSharedViewModel = { entry -> entry.sharedViewModel(navController) }, + getSharedViewModel = { entry -> entry.sharedViewModel() }, ) } - giftScreen(navController = navController) + giftScreen() hostedShowScreen( - navController = navController, onClickShow = onClickQrScan, ) - paymentCompleteScreen(navController = navController) - giftCompleteScreen(navController = navController) - businessScreen(navController = navController) - accountSettingScreen(navController = navController) - profileScreen(navController = navController) + paymentCompleteScreen() + giftCompleteScreen() + businessScreen() + accountSettingScreen() + profileScreen() navigation( startDestination = ProfileRoute.ProfileEdit, ) { - profileEditScreen(navController = navController) - profileSnsEditScreen(navController = navController) - profileLinkEditScreen(navController = navController) + profileEditScreen() + profileSnsEditScreen() + profileLinkEditScreen() } - linkListScreen(navController = navController) - performedShowsScreen(navController = navController) + linkListScreen() + performedShowsScreen() - addShowRegistration( - modifier = modifier, - popBackStack = navController::popBackStack, - ) + addShowRegistration() } } @Composable inline fun NavBackStackEntry.sharedViewModel( - navController: NavController, + navController: NavController = LocalNavController.current, ): T { val navGraphRoute = destination.parent?.route ?: return hiltViewModel() val parentEntry = remember(this) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt index 5a3788711..3d285be6a 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt @@ -1,14 +1,13 @@ package com.nexters.boolti.presentation.screen.accountsetting import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute -fun NavGraphBuilder.accountSettingScreen( - navController: NavHostController, -) { +fun NavGraphBuilder.accountSettingScreen() { composable { + val navController = LocalNavController.current AccountSettingScreen( navigateBack = navController::popBackStack, onClickResign = { navController.navigate(MainRoute.SignOut) }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt index aee2f8b9d..63f9a35ec 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/business/BusinessNavigation.kt @@ -1,16 +1,15 @@ package com.nexters.boolti.presentation.screen.business import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute -fun NavGraphBuilder.businessScreen( - navController: NavHostController, -) { +fun NavGraphBuilder.businessScreen() { composable { + val navController = LocalNavController.current BusinessScreen( onBackPressed = navController::popBackStack ) } -} \ No newline at end of file +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt index 1ad36429e..3bc8a7716 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftNavigation.kt @@ -2,15 +2,15 @@ package com.nexters.boolti.presentation.screen.gift import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.giftScreen( - navController: NavHostController, modifier: Modifier = Modifier, ) { composable { + val navController = LocalNavController.current GiftScreen( modifier = modifier, popBackStack = navController::popBackStack, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt index 1196cac17..9ce501110 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt @@ -1,16 +1,15 @@ package com.nexters.boolti.presentation.screen.giftcomplete import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.extension.navigateToHome +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.navigation.ShowRoute -fun NavGraphBuilder.giftCompleteScreen( - navController: NavHostController, -) { +fun NavGraphBuilder.giftCompleteScreen() { composable { + val navController = LocalNavController.current GiftCompleteScreen( onClickClose = { navController.popBackStack(false) }, onClickHome = navController::navigateToHome, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index 3713cbcad..3be2bbd4c 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -2,16 +2,16 @@ package com.nexters.boolti.presentation.screen.home import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.homeScreen( - navController: NavHostController, modifier: Modifier = Modifier, ) { composable { + val navController = LocalNavController.current HomeScreen( modifier = modifier, navigateToShowDetail = { navController.navigate(MainRoute.ShowDetail(showId = it)) }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListNavigation.kt index 09593f1c0..dc33a4af5 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListNavigation.kt @@ -1,16 +1,16 @@ package com.nexters.boolti.presentation.screen.link import androidx.compose.ui.Modifier -import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.linkListScreen( - navController: NavController, modifier: Modifier = Modifier, ) { composable { + val navController = LocalNavController.current LinkListScreen( modifier = modifier, onClickBack = navController::popBackStack, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt index 40e3b3330..2de37b883 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginNavigation.kt @@ -2,15 +2,15 @@ package com.nexters.boolti.presentation.screen.login import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.loginScreen( - navController: NavHostController, modifier: Modifier = Modifier, ) { composable { + val navController = LocalNavController.current LoginScreen( modifier = modifier, onBackPressed = navController::popBackStack diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt index b41046017..820a7dec8 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt @@ -2,16 +2,15 @@ package com.nexters.boolti.presentation.screen.payment import androidx.core.net.toUri import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable import androidx.navigation.toRoute import com.nexters.boolti.presentation.extension.navigateToHome +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute -fun NavGraphBuilder.paymentCompleteScreen( - navController: NavHostController, -) { +fun NavGraphBuilder.paymentCompleteScreen() { composable { entry -> + val navController = LocalNavController.current val route = entry.toRoute() val showId = route.showId diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt index a46320f67..964a69ac6 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt @@ -1,16 +1,16 @@ package com.nexters.boolti.presentation.screen.perforemdshows import androidx.compose.ui.Modifier -import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.performedShowsScreen( - navController: NavController, modifier: Modifier = Modifier, ) { composable { + val navController = LocalNavController.current PerformedShowsScreen( modifier = modifier, onClickShow = { show -> diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt index b860a2c87..890cbe71b 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt @@ -2,16 +2,16 @@ package com.nexters.boolti.presentation.screen.profile import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.navigation.ProfileRoute fun NavGraphBuilder.profileScreen( - navController: NavHostController, modifier: Modifier = Modifier, ) { composable { + val navController = LocalNavController.current ProfileScreen( modifier = modifier, onClickBack = navController::popBackStack, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt index 2b1652f7b..c6845c3e4 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/ProfileLinkEditNavigation.kt @@ -2,15 +2,15 @@ package com.nexters.boolti.presentation.screen.profileedit.link import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.ProfileRoute fun NavGraphBuilder.profileLinkEditScreen( - navController: NavHostController, modifier: Modifier = Modifier, ) { composable { + val navController = LocalNavController.current LinkEditScreen( modifier = modifier, onAddLink = { linkName, url -> diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt index 8a89a272e..b283dda52 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditNavigation.kt @@ -2,10 +2,10 @@ package com.nexters.boolti.presentation.screen.profileedit.profile import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.domain.model.Link import com.nexters.boolti.domain.model.Sns +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.ProfileRoute import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNotNull @@ -14,10 +14,11 @@ import kotlinx.coroutines.flow.zip import java.util.UUID fun NavGraphBuilder.profileEditScreen( - navController: NavHostController, modifier: Modifier = Modifier, ) { composable { backStackEntry -> + val navController = LocalNavController.current + // 새 링크 추가 val newLinkName = backStackEntry.savedStateHandle .getStateFlow("newLinkName", null).filterNotNull() diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/ProfileSnsEditNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/ProfileSnsEditNavigation.kt index 401a258e9..8432bfbe9 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/ProfileSnsEditNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/ProfileSnsEditNavigation.kt @@ -1,16 +1,16 @@ package com.nexters.boolti.presentation.screen.profileedit.sns import androidx.compose.ui.Modifier -import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.ProfileRoute fun NavGraphBuilder.profileSnsEditScreen( - navController: NavController, modifier: Modifier = Modifier, ) { composable { + val navController = LocalNavController.current SnsEditScreen( modifier = modifier, onAddSns = { type, username -> diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt index e32959f3b..079f6a995 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowNavigation.kt @@ -2,16 +2,16 @@ package com.nexters.boolti.presentation.screen.qr import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.hostedShowScreen( - navController: NavHostController, onClickShow: (showId: String, showName: String) -> Unit, modifier: Modifier = Modifier, ) { composable { + val navController = LocalNavController.current HostedShowScreen( modifier = modifier, onClickShow = onClickShow, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt index 1d39b05ea..9589f7a17 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt @@ -39,11 +39,11 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.domain.model.TicketState import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.component.InstagramIndicator +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.ticket.detail.TicketDetailViewModel import com.nexters.boolti.presentation.theme.Grey10 @@ -55,11 +55,11 @@ import com.nexters.boolti.presentation.theme.point4 import com.nexters.boolti.presentation.util.rememberQrBitmapPainter fun NavGraphBuilder.qrFullScreen( - navController: NavHostController, getSharedViewModel: @Composable (NavBackStackEntry) -> TicketDetailViewModel, modifier: Modifier = Modifier, ) { composable { entry -> + val navController = LocalNavController.current QrFullScreen( modifier = modifier, viewModel = getSharedViewModel(entry), diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt index 609144e77..b0d4319eb 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundNavigation.kt @@ -1,16 +1,14 @@ package com.nexters.boolti.presentation.screen.refund import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable import androidx.navigation.toRoute -import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute -fun NavGraphBuilder.refundScreen( - navController: NavHostController, -) { +fun NavGraphBuilder.refundScreen() { composable { entry -> + val navController = LocalNavController.current val route = entry.toRoute() RefundScreen( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt index 6e98d6dc5..580ae8731 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt @@ -2,21 +2,20 @@ package com.nexters.boolti.presentation.screen.report import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.extension.navigateToHome +import com.nexters.boolti.presentation.screen.LocalNavController fun NavGraphBuilder.reportScreen( - navController: NavHostController, - navigateToHome: () -> Unit, - popBackStack: () -> Unit, modifier: Modifier = Modifier, ) { composable( route = "report/{showId}", ) { + val navController = LocalNavController.current ReportScreen( - onBackPressed = popBackStack, - popupToHome = navigateToHome, + onBackPressed = navController::popBackStack, + popupToHome = navController::navigateToHome, modifier = modifier, ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt index c8ddee62f..463b7d057 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsNavigation.kt @@ -1,14 +1,13 @@ package com.nexters.boolti.presentation.screen.reservations import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute -fun NavGraphBuilder.reservationsScreen( - navController: NavHostController, -) { +fun NavGraphBuilder.reservationsScreen() { composable { + val navController = LocalNavController.current ReservationsScreen( onBackPressed = navController::popBackStack, navigateToDetail = { id, isGift -> @@ -18,6 +17,7 @@ fun NavGraphBuilder.reservationsScreen( isGift = isGift ) ) - }) + }, + ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt index 8d8352983..303f24791 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt @@ -4,24 +4,23 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController fun NavGraphBuilder.showDetailContentScreen( - navController: NavHostController, - popBackStack: () -> Unit, getSharedViewModel: @Composable (NavBackStackEntry) -> ShowDetailViewModel, modifier: Modifier = Modifier, ) { composable( route = "content", ) { entry -> + val navController = LocalNavController.current val showViewModel: ShowDetailViewModel = getSharedViewModel(entry) ShowDetailContentScreen( modifier = modifier, viewModel = showViewModel, - onBackPressed = popBackStack + onBackPressed = navController::popBackStack, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt index e7c242636..63eab7138 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt @@ -4,26 +4,25 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.extension.navigateToHome +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.navigation.ShowRoute fun NavGraphBuilder.showDetailScreen( - navController: NavHostController, navigateTo: (String) -> Unit, - popBackStack: () -> Unit, - navigateToHome: () -> Unit, getSharedViewModel: @Composable (NavBackStackEntry) -> ShowDetailViewModel, modifier: Modifier = Modifier, ) { composable { entry -> val showViewModel: ShowDetailViewModel = getSharedViewModel(entry) + val navController = LocalNavController.current ShowDetailScreen( modifier = modifier, - onBack = popBackStack, - onClickHome = navigateToHome, + onBack = navController::popBackStack, + onClickHome = navController::navigateToHome, onClickContent = { navigateTo("content") }, onTicketSelected = { showId, ticketId, ticketCount, isInviteTicket -> navController.navigate( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt index 02aa2dd22..e13b84173 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt @@ -3,27 +3,26 @@ package com.nexters.boolti.presentation.screen.showdetail import androidx.compose.runtime.Composable import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.composable import androidx.navigation.navArgument +import com.nexters.boolti.presentation.screen.LocalNavController fun NavGraphBuilder.showImagesScreen( - navController: NavHostController, - popBackStack: () -> Unit, getSharedViewModel: @Composable (NavBackStackEntry) -> ShowDetailViewModel, ) { composable( route = "images/{index}", arguments = listOf(navArgument("index") { type = NavType.IntType }), ) { entry -> + val navController = LocalNavController.current val showViewModel: ShowDetailViewModel = getSharedViewModel(entry) val index = entry.arguments!!.getInt("index") ShowImagesScreen( index = index, viewModel = showViewModel, - onBackPressed = popBackStack, + onBackPressed = navController::popBackStack, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationNavigation.kt index 991e46599..23b514328 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationNavigation.kt @@ -3,18 +3,19 @@ package com.nexters.boolti.presentation.screen.showregistration import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.MainDestination fun NavGraphBuilder.addShowRegistration( modifier: Modifier = Modifier, - popBackStack: () -> Unit, ) { composable( route = MainDestination.ShowRegistration.route, ) { + val navController = LocalNavController.current ShowRegistrationScreen( modifier = modifier, - onClickBack = popBackStack, + onClickBack = navController::popBackStack, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt index e778aee9e..4d49a8f7d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutNavigation.kt @@ -1,16 +1,14 @@ package com.nexters.boolti.presentation.screen.signout import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.nexters.boolti.presentation.extension.navigateToHome -import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute -fun NavGraphBuilder.signoutScreen( - navController: NavHostController, -) { +fun NavGraphBuilder.signoutScreen() { composable { + val navController = LocalNavController.current SignoutScreen( navigateToHome = navController::navigateToHome, navigateBack = navController::popBackStack, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt index de6c18b2c..d5dff49fe 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt @@ -82,7 +82,6 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable import coil.compose.AsyncImage import com.nexters.boolti.domain.model.TicketGroup @@ -95,6 +94,7 @@ import com.nexters.boolti.presentation.component.InstagramIndicator import com.nexters.boolti.presentation.component.ShowInquiry import com.nexters.boolti.presentation.extension.toDp import com.nexters.boolti.presentation.extension.toPx +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.LocalSnackbarController import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.qr.QrCoverView @@ -114,17 +114,16 @@ import com.nexters.boolti.presentation.util.asyncImageBlurModel import com.nexters.boolti.presentation.util.rememberQrBitmapPainter fun NavGraphBuilder.ticketDetailScreen( - navController: NavHostController, - popBackStack: () -> Unit, getSharedViewModel: @Composable (NavBackStackEntry) -> TicketDetailViewModel, modifier: Modifier = Modifier, ) { composable( route = "detail", ) { entry -> + val navController = LocalNavController.current TicketDetailScreen( modifier = modifier, - onBackClicked = popBackStack, + onBackClicked = navController::popBackStack, onClickQr = { navController.navigate(MainRoute.Qr) }, navigateToShowDetail = { navController.navigate(MainRoute.ShowDetail(showId = it)) }, viewModel = getSharedViewModel(entry), diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt index 65f8a2894..1c1b2ad69 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingNavigation.kt @@ -2,15 +2,15 @@ package com.nexters.boolti.presentation.screen.ticketing import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute fun NavGraphBuilder.ticketingScreen( - navController: NavHostController, modifier: Modifier = Modifier, ) { composable { + val navController = LocalNavController.current TicketingScreen( modifier = modifier, onBackClicked = navController::popBackStack, From 11a322437ad7ab9154e295ff7804b46a5b419dfc Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 20:32:49 +0900 Subject: [PATCH 65/90] =?UTF-8?q?refactor/253=20=ED=8B=B0=EC=BC=93=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=ED=99=94=EB=A9=B4=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nexters/boolti/presentation/screen/Main.kt | 7 +++---- .../presentation/screen/MainDestination.kt | 9 --------- .../presentation/screen/home/HomeNavigation.kt | 3 ++- .../screen/navigation/MainRoute.kt | 18 ------------------ .../screen/navigation/TicketRoute.kt | 16 ++++++++++++++++ .../presentation/screen/qr/QrFullScreen.kt | 4 ++-- .../screen/ticket/detail/TicketDetailScreen.kt | 7 +++---- 7 files changed, 26 insertions(+), 38 deletions(-) create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/TicketRoute.kt diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 7113ce928..21af1adfa 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -34,6 +34,7 @@ import com.nexters.boolti.presentation.screen.login.loginScreen import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.navigation.ProfileRoute import com.nexters.boolti.presentation.screen.navigation.ShowRoute +import com.nexters.boolti.presentation.screen.navigation.TicketRoute import com.nexters.boolti.presentation.screen.payment.paymentCompleteScreen import com.nexters.boolti.presentation.screen.perforemdshows.performedShowsScreen import com.nexters.boolti.presentation.screen.profile.profileScreen @@ -141,16 +142,14 @@ fun MainNavigation( ticketingScreen() - navigation( - route = "${MainDestination.TicketDetail.route}/{$ticketId}", + navigation( + startDestination = TicketRoute.TicketDetail, deepLinks = listOf( navDeepLink { uriPattern = "https://app.boolti.in/tickets/{ticketId}" action = Intent.ACTION_VIEW } ), - startDestination = "detail", - arguments = MainDestination.TicketDetail.arguments, ) { ticketDetailScreen( getSharedViewModel = { entry -> entry.sharedViewModel() }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index 6e24e77ab..f20c38bbc 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -1,14 +1,6 @@ package com.nexters.boolti.presentation.screen -import androidx.navigation.NavType -import androidx.navigation.navArgument - sealed class MainDestination(val route: String) { - - data object TicketDetail : MainDestination(route = "tickets") { - val arguments = listOf(navArgument(ticketId) { type = NavType.StringType }) - } - data object ShowRegistration : MainDestination(route = "webView") } @@ -16,4 +8,3 @@ sealed class MainDestination(val route: String) { * arguments */ const val showId = "showId" -const val ticketId = "ticketId" diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index 3be2bbd4c..cfc3ebaaa 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -6,6 +6,7 @@ import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute +import com.nexters.boolti.presentation.screen.navigation.TicketRoute fun NavGraphBuilder.homeScreen( modifier: Modifier = Modifier, @@ -15,7 +16,7 @@ fun NavGraphBuilder.homeScreen( HomeScreen( modifier = modifier, navigateToShowDetail = { navController.navigate(MainRoute.ShowDetail(showId = it)) }, - navigateToTicketDetail = { navController.navigate("${MainDestination.TicketDetail.route}/$it") }, + navigateToTicketDetail = { navController.navigate(TicketRoute.TicketRoot(ticketId = it)) }, navigateToQrScan = { navController.navigate(MainRoute.HostedShows) }, navigateToAccountSetting = { navController.navigate(MainRoute.AccountSetting) }, navigateToReservations = { navController.navigate(MainRoute.Reservations) }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt index c9a278838..bb1f471e7 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt @@ -37,14 +37,6 @@ sealed interface MainRoute { val giftId: String, ) : MainRoute - @Serializable - data class TicketDetail( - val ticketId: String, - ) - - @Serializable - data object Qr : MainRoute - @Serializable data object Reservations : MainRoute @@ -80,16 +72,6 @@ sealed interface MainRoute { val userCode: String? = null ) : MainRoute - @Serializable - data object ProfileEdit : MainRoute - - @Serializable - data class ProfileLinkEdit( - val linkId: String?, - val linkTitle: String?, - val url: String? - ) : MainRoute - @Serializable data class LinkList( val userCode: String? = null, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/TicketRoute.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/TicketRoute.kt new file mode 100644 index 000000000..fb1653461 --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/TicketRoute.kt @@ -0,0 +1,16 @@ +package com.nexters.boolti.presentation.screen.navigation + +import kotlinx.serialization.Serializable + +sealed interface TicketRoute { + @Serializable + data class TicketRoot( + val ticketId: String, + ) : TicketRoute + + @Serializable + data object TicketDetail : TicketRoute + + @Serializable + data object Qr : TicketRoute +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt index 9589f7a17..c5ed47637 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrFullScreen.kt @@ -44,7 +44,7 @@ import com.nexters.boolti.domain.model.TicketState import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.component.InstagramIndicator import com.nexters.boolti.presentation.screen.LocalNavController -import com.nexters.boolti.presentation.screen.navigation.MainRoute +import com.nexters.boolti.presentation.screen.navigation.TicketRoute import com.nexters.boolti.presentation.screen.ticket.detail.TicketDetailViewModel import com.nexters.boolti.presentation.theme.Grey10 import com.nexters.boolti.presentation.theme.Grey20 @@ -58,7 +58,7 @@ fun NavGraphBuilder.qrFullScreen( getSharedViewModel: @Composable (NavBackStackEntry) -> TicketDetailViewModel, modifier: Modifier = Modifier, ) { - composable { entry -> + composable { entry -> val navController = LocalNavController.current QrFullScreen( modifier = modifier, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt index d5dff49fe..2b744f74b 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt @@ -97,6 +97,7 @@ import com.nexters.boolti.presentation.extension.toPx import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.LocalSnackbarController import com.nexters.boolti.presentation.screen.navigation.MainRoute +import com.nexters.boolti.presentation.screen.navigation.TicketRoute import com.nexters.boolti.presentation.screen.qr.QrCoverView import com.nexters.boolti.presentation.theme.BooltiTheme import com.nexters.boolti.presentation.theme.Grey10 @@ -117,14 +118,12 @@ fun NavGraphBuilder.ticketDetailScreen( getSharedViewModel: @Composable (NavBackStackEntry) -> TicketDetailViewModel, modifier: Modifier = Modifier, ) { - composable( - route = "detail", - ) { entry -> + composable { entry -> val navController = LocalNavController.current TicketDetailScreen( modifier = modifier, onBackClicked = navController::popBackStack, - onClickQr = { navController.navigate(MainRoute.Qr) }, + onClickQr = { navController.navigate(TicketRoute.Qr) }, navigateToShowDetail = { navController.navigate(MainRoute.ShowDetail(showId = it)) }, viewModel = getSharedViewModel(entry), ) From eb2e04c3c847185c2f4932a6e4bea02b1f32928d Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 21:01:48 +0900 Subject: [PATCH 66/90] =?UTF-8?q?refactor/253=20=EA=B3=B5=EC=97=B0=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=ED=99=94=EB=A9=B4=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/Main.kt | 7 ++---- .../presentation/screen/MainDestination.kt | 5 ---- .../giftcomplete/GiftCompleteNavigation.kt | 2 +- .../screen/home/HomeNavigation.kt | 3 ++- .../screen/navigation/MainRoute.kt | 5 ---- .../screen/navigation/ShowRoute.kt | 23 ++++++++++++++++--- .../screen/payment/PaymentNavigation.kt | 5 ++-- .../PerformedShowsNavigation.kt | 3 ++- .../screen/profile/ProfileNavigation.kt | 3 ++- .../screen/report/ReportNavigation.kt | 5 ++-- .../screen/report/ReportViewModel.kt | 10 ++++---- .../showdetail/ShowDetailContentNavigation.kt | 5 ++-- .../screen/showdetail/ShowDetailNavigation.kt | 7 +++--- .../screen/showdetail/ShowDetailViewModel.kt | 4 +++- .../screen/showdetail/ShowImagesNavigation.kt | 11 ++++----- .../ticket/detail/TicketDetailScreen.kt | 4 ++-- 16 files changed, 52 insertions(+), 50 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 21af1adfa..1b79ad029 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -118,17 +118,16 @@ fun MainNavigation( reservationDetailScreen() refundScreen() - navigation( + navigation( startDestination = ShowRoute.Detail, deepLinks = listOf( navDeepLink { - uriPattern = "https://preview.boolti.in/show/{$showId}" + uriPattern = "https://preview.boolti.in/show/{showId}" action = Intent.ACTION_VIEW }, ), ) { showDetailScreen( - navigateTo = navController::navigateTo, getSharedViewModel = { entry -> entry.sharedViewModel() } ) showImagesScreen( @@ -195,5 +194,3 @@ inline fun NavBackStackEntry.sharedViewModel( } return hiltViewModel(parentEntry) } - -private fun NavController.navigateTo(route: String) = navigate(route) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index f20c38bbc..f093df12c 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -3,8 +3,3 @@ package com.nexters.boolti.presentation.screen sealed class MainDestination(val route: String) { data object ShowRegistration : MainDestination(route = "webView") } - -/** - * arguments - */ -const val showId = "showId" diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt index 9ce501110..3a2af0b9d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteNavigation.kt @@ -23,4 +23,4 @@ fun NavGraphBuilder.giftCompleteScreen() { }, ) } -} \ No newline at end of file +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index cfc3ebaaa..5365261e7 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -6,6 +6,7 @@ import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.MainDestination import com.nexters.boolti.presentation.screen.navigation.MainRoute +import com.nexters.boolti.presentation.screen.navigation.ShowRoute import com.nexters.boolti.presentation.screen.navigation.TicketRoute fun NavGraphBuilder.homeScreen( @@ -15,7 +16,7 @@ fun NavGraphBuilder.homeScreen( val navController = LocalNavController.current HomeScreen( modifier = modifier, - navigateToShowDetail = { navController.navigate(MainRoute.ShowDetail(showId = it)) }, + navigateToShowDetail = { navController.navigate(ShowRoute.ShowRoot(showId = it)) }, navigateToTicketDetail = { navController.navigate(TicketRoute.TicketRoot(ticketId = it)) }, navigateToQrScan = { navController.navigate(MainRoute.HostedShows) }, navigateToAccountSetting = { navController.navigate(MainRoute.AccountSetting) }, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt index bb1f471e7..5dd979984 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/MainRoute.kt @@ -6,11 +6,6 @@ sealed interface MainRoute { @Serializable data object Home : MainRoute - @Serializable - data class ShowDetail( - val showId: String, - ) : MainRoute - @Serializable data class Ticketing( val showId: String, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ShowRoute.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ShowRoute.kt index 8cbd3bf90..57b2d4376 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ShowRoute.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/navigation/ShowRoute.kt @@ -2,8 +2,25 @@ package com.nexters.boolti.presentation.screen.navigation import kotlinx.serialization.Serializable -// todo : 리팩토링 예정 sealed interface ShowRoute { @Serializable - data object Detail -} \ No newline at end of file + data class ShowRoot( + val showId: String, + ) : ShowRoute + + @Serializable + data object Detail : ShowRoute + + @Serializable + data class Images( + val index: Int, + ) : ShowRoute + + @Serializable + data object Content : ShowRoute + + @Serializable + data class Report( + val showId: String?, + ) : ShowRoute +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt index 820a7dec8..d0e5b1dc8 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/payment/PaymentNavigation.kt @@ -7,6 +7,7 @@ import androidx.navigation.toRoute import com.nexters.boolti.presentation.extension.navigateToHome import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute +import com.nexters.boolti.presentation.screen.navigation.ShowRoute fun NavGraphBuilder.paymentCompleteScreen() { composable { entry -> @@ -17,8 +18,8 @@ fun NavGraphBuilder.paymentCompleteScreen() { PaymentCompleteScreen( onClickHome = navController::navigateToHome, onClickClose = { - navController.popBackStack(inclusive = true) - navController.navigate(MainRoute.ShowDetail(showId = showId)) + navController.popBackStack(inclusive = true) + navController.navigate(ShowRoute.ShowRoot(showId = showId)) }, navigateToReservation = { reservation -> navController.navigate( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt index 964a69ac6..dbba24659 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsNavigation.kt @@ -5,6 +5,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute +import com.nexters.boolti.presentation.screen.navigation.ShowRoute fun NavGraphBuilder.performedShowsScreen( modifier: Modifier = Modifier, @@ -14,7 +15,7 @@ fun NavGraphBuilder.performedShowsScreen( PerformedShowsScreen( modifier = modifier, onClickShow = { show -> - navController.navigate(MainRoute.ShowDetail(showId = show.id)) + navController.navigate(ShowRoute.ShowRoot(showId = show.id)) }, onClickBack = navController::popBackStack, ) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt index 890cbe71b..10464a335 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileNavigation.kt @@ -6,6 +6,7 @@ import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.navigation.ProfileRoute +import com.nexters.boolti.presentation.screen.navigation.ShowRoute fun NavGraphBuilder.profileScreen( modifier: Modifier = Modifier, @@ -22,7 +23,7 @@ fun NavGraphBuilder.profileScreen( navController.navigate(MainRoute.PerformedShows(userCode)) }, navigateToProfileEdit = { navController.navigate(ProfileRoute.ProfileEdit) }, - navigateToShow = { showId -> navController.navigate(MainRoute.ShowDetail(showId = showId)) }, + navigateToShow = { showId -> navController.navigate(ShowRoute.ShowRoot(showId = showId)) }, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt index 580ae8731..5fdcd7e09 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportNavigation.kt @@ -5,13 +5,12 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.extension.navigateToHome import com.nexters.boolti.presentation.screen.LocalNavController +import com.nexters.boolti.presentation.screen.navigation.ShowRoute fun NavGraphBuilder.reportScreen( modifier: Modifier = Modifier, ) { - composable( - route = "report/{showId}", - ) { + composable { val navController = LocalNavController.current ReportScreen( onBackPressed = navController::popBackStack, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportViewModel.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportViewModel.kt index 909bc41d9..e41016af9 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportViewModel.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/report/ReportViewModel.kt @@ -2,14 +2,12 @@ package com.nexters.boolti.presentation.screen.report import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import androidx.navigation.toRoute +import com.nexters.boolti.presentation.screen.navigation.ShowRoute import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import javax.inject.Inject @@ -17,7 +15,7 @@ import javax.inject.Inject class ReportViewModel @Inject constructor( savedStateHandle: SavedStateHandle, ) : ViewModel() { - private val showId: String = checkNotNull(savedStateHandle["showId"]) { + private val showId: String = checkNotNull(savedStateHandle.toRoute().showId) { "showId가 전달되어야 합니다." } @@ -27,4 +25,4 @@ class ReportViewModel @Inject constructor( fun updateReason(newReason: String) { _uiState.update { it.copy(reason = newReason) } } -} \ No newline at end of file +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt index 303f24791..f07302cff 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentNavigation.kt @@ -6,14 +6,13 @@ import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.LocalNavController +import com.nexters.boolti.presentation.screen.navigation.ShowRoute fun NavGraphBuilder.showDetailContentScreen( getSharedViewModel: @Composable (NavBackStackEntry) -> ShowDetailViewModel, modifier: Modifier = Modifier, ) { - composable( - route = "content", - ) { entry -> + composable { entry -> val navController = LocalNavController.current val showViewModel: ShowDetailViewModel = getSharedViewModel(entry) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt index 63eab7138..f9c52224a 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt @@ -11,7 +11,6 @@ import com.nexters.boolti.presentation.screen.navigation.MainRoute import com.nexters.boolti.presentation.screen.navigation.ShowRoute fun NavGraphBuilder.showDetailScreen( - navigateTo: (String) -> Unit, getSharedViewModel: @Composable (NavBackStackEntry) -> ShowDetailViewModel, modifier: Modifier = Modifier, ) { @@ -23,7 +22,7 @@ fun NavGraphBuilder.showDetailScreen( modifier = modifier, onBack = navController::popBackStack, onClickHome = navController::navigateToHome, - onClickContent = { navigateTo("content") }, + onClickContent = { navController.navigate(ShowRoute.Content) }, onTicketSelected = { showId, ticketId, ticketCount, isInviteTicket -> navController.navigate( MainRoute.Ticketing( @@ -45,10 +44,10 @@ fun NavGraphBuilder.showDetailScreen( }, viewModel = showViewModel, navigateToLogin = { navController.navigate(MainRoute.Login) }, - navigateToImages = { index -> navigateTo("images/$index") }, + navigateToImages = { index -> navController.navigate(ShowRoute.Images(index)) }, navigateToReport = { val showId = entry.arguments?.getString("showId") - navigateTo("report/$showId") + navController.navigate(ShowRoute.Report(showId)) }, navigateToProfile = { userCode -> navController.navigate(MainRoute.Profile(userCode = userCode)) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailViewModel.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailViewModel.kt index 9af9ba3f2..c4325fe1c 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailViewModel.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailViewModel.kt @@ -3,10 +3,12 @@ package com.nexters.boolti.presentation.screen.showdetail import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.navigation.toRoute import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.ktx.Firebase import com.nexters.boolti.domain.repository.AuthRepository import com.nexters.boolti.domain.repository.ShowRepository +import com.nexters.boolti.presentation.screen.navigation.ShowRoute import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -27,7 +29,7 @@ class ShowDetailViewModel @Inject constructor( private val showRepository: ShowRepository, authRepository: AuthRepository, ) : ViewModel() { - val showId: String = checkNotNull(savedStateHandle["showId"]) + val showId: String = checkNotNull(savedStateHandle.toRoute().showId) private val _uiState = MutableStateFlow(ShowDetailUiState()) val uiState: StateFlow = _uiState.asStateFlow() diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt index e13b84173..6588f9de0 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowImagesNavigation.kt @@ -3,21 +3,18 @@ package com.nexters.boolti.presentation.screen.showdetail import androidx.compose.runtime.Composable import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavType import androidx.navigation.compose.composable -import androidx.navigation.navArgument +import androidx.navigation.toRoute import com.nexters.boolti.presentation.screen.LocalNavController +import com.nexters.boolti.presentation.screen.navigation.ShowRoute fun NavGraphBuilder.showImagesScreen( getSharedViewModel: @Composable (NavBackStackEntry) -> ShowDetailViewModel, ) { - composable( - route = "images/{index}", - arguments = listOf(navArgument("index") { type = NavType.IntType }), - ) { entry -> + composable { entry -> val navController = LocalNavController.current val showViewModel: ShowDetailViewModel = getSharedViewModel(entry) - val index = entry.arguments!!.getInt("index") + val index = entry.toRoute().index ShowImagesScreen( index = index, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt index 2b744f74b..55717c384 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt @@ -96,7 +96,7 @@ import com.nexters.boolti.presentation.extension.toDp import com.nexters.boolti.presentation.extension.toPx import com.nexters.boolti.presentation.screen.LocalNavController import com.nexters.boolti.presentation.screen.LocalSnackbarController -import com.nexters.boolti.presentation.screen.navigation.MainRoute +import com.nexters.boolti.presentation.screen.navigation.ShowRoute import com.nexters.boolti.presentation.screen.navigation.TicketRoute import com.nexters.boolti.presentation.screen.qr.QrCoverView import com.nexters.boolti.presentation.theme.BooltiTheme @@ -124,7 +124,7 @@ fun NavGraphBuilder.ticketDetailScreen( modifier = modifier, onBackClicked = navController::popBackStack, onClickQr = { navController.navigate(TicketRoute.Qr) }, - navigateToShowDetail = { navController.navigate(MainRoute.ShowDetail(showId = it)) }, + navigateToShowDetail = { navController.navigate(ShowRoute.ShowRoot(showId = it)) }, viewModel = getSharedViewModel(entry), ) } From a5c5217e7d135e5c716f907d31155500bd202d25 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 21:04:27 +0900 Subject: [PATCH 67/90] =?UTF-8?q?refactor/253=20todo=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/nexters/boolti/presentation/screen/MainDestination.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index f093df12c..3002e44fe 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -1,5 +1,6 @@ package com.nexters.boolti.presentation.screen sealed class MainDestination(val route: String) { + // TODO 브릿지 작업 머지 후 작업 data object ShowRegistration : MainDestination(route = "webView") } From 97897acdab78c8e0b9117d8d49bab7d1c7e26f41 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 22:14:39 +0900 Subject: [PATCH 68/90] =?UTF-8?q?feature/Boolti-343=20QR=20=EC=8A=A4?= =?UTF-8?q?=EC=BA=94=20=EC=83=81=ED=83=9C=EB=B0=94=20=EC=83=89=EC=83=81=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/nexters/boolti/presentation/QrScanActivity.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt b/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt index 69742bd34..404bd2ed6 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt @@ -57,9 +57,8 @@ class QrScanActivity : ComponentActivity() { requestPermission(Manifest.permission.CAMERA, 100) - enableEdgeToEdge( - statusBarStyle = SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT), - ) + enableEdgeToEdge(statusBarStyle = SystemBarStyle.dark(Color.TRANSPARENT)) + setContent { BooltiTheme { QrScanScreen( From e883189819d41645e29f64571517e64506ff50a3 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 22:20:05 +0900 Subject: [PATCH 69/90] =?UTF-8?q?feature/Boolti-343=20=ED=8B=B0=EC=BC=93?= =?UTF-8?q?=20=EC=84=A0=ED=83=9D=20=EB=B0=94=ED=85=80=EC=8B=9C=ED=8A=B8=20?= =?UTF-8?q?=ED=95=98=EB=8B=A8=20=ED=8C=A8=EB=94=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../screen/ticketing/ChooseTicketBottomSheet.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/ChooseTicketBottomSheet.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/ChooseTicketBottomSheet.kt index 5d452aeef..1612998a0 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/ChooseTicketBottomSheet.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/ChooseTicketBottomSheet.kt @@ -99,10 +99,8 @@ fun ChooseTicketBottomSheet( contentColor = MaterialTheme.colorScheme.surfaceTint, containerColor = Grey85, ) { - Column( - modifier = Modifier - .padding(bottom = 20.dp) - .heightIn(max = 564.dp) + Box( + modifier = Modifier.heightIn(max = 564.dp) ) { uiState.selected?.let { ChooseTicketBottomSheetContent2( @@ -138,7 +136,9 @@ private fun ChooseTicketBottomSheetContent1( onSelectItem: (TicketWithQuantity) -> Unit, ) { val listState = rememberLazyListState() - Column { + Column( + modifier = Modifier.padding(bottom = 20.dp), + ) { Text( text = stringResource(id = R.string.choose_ticket_bottomsheet_title), style = MaterialTheme.typography.titleLarge.copy(color = Grey30), From 982794ab4650356eb931a4d1a240d9827e1c57b1 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 22:52:24 +0900 Subject: [PATCH 70/90] =?UTF-8?q?feature/Boolti-343=20=EA=B2=B0=EC=A0=9C?= =?UTF-8?q?=20=EB=82=B4=EC=97=AD,=20=EA=B2=B0=EC=A0=9C=20=EC=B7=A8?= =?UTF-8?q?=EC=86=8C=20=ED=99=94=EB=A9=B4=20=ED=95=98=EB=8B=A8=20=ED=8C=A8?= =?UTF-8?q?=EB=94=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reservationdetail/ReservationDetailScreen.kt | 6 ++++-- .../boolti/presentation/screen/refund/ReasonPage.kt | 5 +++-- .../boolti/presentation/screen/refund/RefundInfoPage.kt | 3 +-- .../boolti/presentation/screen/refund/RefundScreen.kt | 8 +++----- .../boolti/presentation/screen/signout/SignoutScreen.kt | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailScreen.kt index 1d161df28..374b10425 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState @@ -74,7 +75,7 @@ fun ReservationDetailScreen( } Scaffold( - modifier = modifier, + modifier = modifier.navigationBarsPadding(), topBar = { BtBackAppBar( title = stringResource(id = R.string.reservation_detail), @@ -143,7 +144,8 @@ fun ReservationDetailScreen( if (state.reservation.isRefundable) { MainButton( modifier = modifier - .padding(horizontal = marginHorizontal, vertical = 8.dp) + .padding(horizontal = marginHorizontal) + .padding(top = 8.dp, bottom = 20.dp) .fillMaxWidth(), colors = MainButtonDefaults.buttonColors( containerColor = Grey15, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/ReasonPage.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/ReasonPage.kt index 72bfc3a7f..d48e9a3fc 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/ReasonPage.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/ReasonPage.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -23,7 +24,7 @@ fun ReasonPage( onNextClick: () -> Unit, modifier: Modifier = Modifier, ) { - Column(modifier = modifier) { + Column(modifier = modifier.navigationBarsPadding()) { Text( modifier = Modifier .padding(top = 20.dp) @@ -48,7 +49,7 @@ fun ReasonPage( modifier = Modifier .fillMaxWidth() .padding(horizontal = marginHorizontal) - .padding(bottom = 8.dp), + .padding(bottom = 20.dp), onClick = onNextClick, enabled = reason.isNotBlank(), label = stringResource(id = R.string.next), diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundInfoPage.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundInfoPage.kt index 43bc1974d..d0a25bec7 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundInfoPage.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundInfoPage.kt @@ -136,8 +136,7 @@ fun RefundInfoPage( modifier = Modifier .fillMaxWidth() .padding(horizontal = marginHorizontal) - .padding(top = 16.dp) - .padding(vertical = 8.dp), + .padding(top = 16.dp, bottom = 20.dp), onClick = onRequest, enabled = uiState.isAbleToRequest, label = stringResource(id = R.string.refund_button) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundScreen.kt index 248d862fa..da6174293 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/refund/RefundScreen.kt @@ -86,13 +86,14 @@ fun RefundScreen( val reservation = uiState.reservation ?: return@Scaffold HorizontalPager( - modifier = Modifier.fillMaxSize(), + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), state = pagerState, userScrollEnabled = false, ) { index -> if (index == 0 && !isGift) { ReasonPage( - modifier = Modifier.padding(innerPadding), onNextClick = { scope.launch { pagerState.animateScrollToPage(1) @@ -105,9 +106,6 @@ fun RefundScreen( RefundInfoPage( uiState = uiState, refundPolicy = refundPolicy, - modifier = Modifier - .fillMaxSize() - .padding(innerPadding), reservation = reservation, onRequest = { openDialog = true }, onRefundPolicyChecked = viewModel::toggleRefundPolicyCheck, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutScreen.kt index d4047f498..5b386f2a3 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/signout/SignoutScreen.kt @@ -50,7 +50,7 @@ fun SignoutScreen( modifier = Modifier .fillMaxWidth() .padding(horizontal = marginHorizontal) - .padding(bottom = 42.dp), + .padding(bottom = 20.dp), label = if (firstPage) stringResource(R.string.next) else stringResource(R.string.signout_button), enabled = firstPage || reason.isNotBlank(), onClick = { From 24bab961541692a35c8c079a08c3a906b0a0cb75 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 23:04:17 +0900 Subject: [PATCH 71/90] =?UTF-8?q?feature/Boolti-343=20=ED=8B=B0=EC=BC=93?= =?UTF-8?q?=20=EC=83=81=EC=84=B8=20=ED=99=94=EB=A9=B4=20=EA=B0=84=EA=B2=A9?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ticket/detail/TicketDetailScreen.kt | 197 +++++++++--------- 1 file changed, 98 insertions(+), 99 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt index 7410599d1..dd23540f7 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt @@ -22,6 +22,7 @@ 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.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -206,7 +207,7 @@ private fun TicketDetailScreen( } Scaffold( - modifier = modifier, + modifier = modifier.navigationBarsPadding(), topBar = { BtBackAppBar( title = stringResource(R.string.ticket_detail_title), @@ -228,6 +229,7 @@ private fun TicketDetailScreen( Column( modifier = Modifier .padding(horizontal = 29.dp) + .padding(bottom = 20.dp) .verticalScroll(scrollState), ) { val ticketShape = TicketShape( @@ -237,7 +239,7 @@ private fun TicketDetailScreen( cornerRadius = 8.dp.toPx(), bottomAreaHeight = ticketSectionHeightUntilTicketInfo, ) - Column( + Box( modifier = Modifier .onGloballyPositioned { coordinates -> contentWidth = coordinates.size.width.toFloat() @@ -254,112 +256,109 @@ private fun TicketDetailScreen( ) ), shape = ticketShape, - ), + ) ) { - Box { - // 배경 블러된 이미지 - Box(contentAlignment = Alignment.BottomCenter) { - AsyncImage( - model = asyncImageBlurModel( - context, - ticketGroup.poster, - radius = 24 - ), - modifier = Modifier - .width(contentWidth.toDp()) - .aspectRatio(317 / 570f) - .alpha(.8f), - contentScale = ContentScale.Crop, - contentDescription = null, - ) - Box( - Modifier - .fillMaxWidth() - .aspectRatio(317 / 125f) - .background( - brush = Brush.verticalGradient( - listOf( - Black.copy(alpha = 0f), - Black - ) - ), - ) - ) - } - Column( + // 배경 블러된 이미지 + Box(contentAlignment = Alignment.BottomCenter) { + AsyncImage( + model = asyncImageBlurModel( + context, + ticketGroup.poster, + radius = 24 + ), modifier = Modifier + .width(contentWidth.toDp()) + .aspectRatio(317 / 570f) + .alpha(.8f), + contentScale = ContentScale.Crop, + contentDescription = null, + ) + Box( + Modifier + .fillMaxWidth() + .aspectRatio(317 / 125f) .background( - brush = Brush.linearGradient( - colors = listOf( - Color(0xCCC5CACD), - Grey95.copy(alpha = .8f) - ), + brush = Brush.verticalGradient( + listOf( + Black.copy(alpha = 0f), + Black + ) ), - shape = ticketShape, ) - ) { - Title(showName = ticketGroup.showName) - - QrCodes( - modifier = Modifier - .fillMaxWidth() - .padding(top = 20.dp) - .padding(horizontal = marginHorizontal), - ticketGroup = ticketGroup, - pagerState = pagerState, - onClickQr = onClickQr, - ) - - DottedDivider( - modifier = Modifier - .fillMaxWidth() - .padding(top = 28.dp) - .padding(horizontal = marginHorizontal), - color = White.copy(alpha = 0.3f), - thickness = 2.dp + ) + } + Column( + modifier = Modifier + .background( + brush = Brush.linearGradient( + colors = listOf( + Color(0xCCC5CACD), + Grey95.copy(alpha = .8f) + ), + ), + shape = ticketShape, ) + ) { + Title(showName = ticketGroup.showName) - Column( - modifier = Modifier - .onGloballyPositioned { coordinates -> - ticketSectionHeightUntilTicketInfo = - coordinates.size.height.toFloat() - } - ) { - Notice(notice = ticketGroup.ticketNotice) - - val copySuccessMessage = - stringResource(id = R.string.ticketing_address_copied_message) - Inquiry( - hostName = ticketGroup.hostName, - hostPhoneNumber = ticketGroup.hostPhoneNumber, - onClickCopyPlace = { - clipboardManager.setText(AnnotatedString(ticketGroup.streetAddress)) - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) { - snackbarHostController.showMessage(copySuccessMessage) - } - }, - onClickNavigateToShowDetail = { - navigateToShowDetail(ticketGroup.showId) - } - ) - } - } + QrCodes( + modifier = Modifier + .fillMaxWidth() + .padding(top = 20.dp) + .padding(horizontal = marginHorizontal), + ticketGroup = ticketGroup, + pagerState = pagerState, + onClickQr = onClickQr, + ) - // 티켓 좌상단 꼭지점 그라데이션 - Box( + DottedDivider( modifier = Modifier - .align(Alignment.TopStart) - .size(105.dp) - .alpha(.45f) - .background( - Brush.linearGradient( - colors = listOf(White, White.copy(alpha = 0f)), - end = Offset(50f, 50f), - ) - ), + .fillMaxWidth() + .padding(top = 28.dp) + .padding(horizontal = marginHorizontal), + color = White.copy(alpha = 0.3f), + thickness = 2.dp ) + + Column( + modifier = Modifier + .onGloballyPositioned { coordinates -> + ticketSectionHeightUntilTicketInfo = + coordinates.size.height.toFloat() + } + ) { + Notice(notice = ticketGroup.ticketNotice) + + val copySuccessMessage = + stringResource(id = R.string.ticketing_address_copied_message) + Inquiry( + hostName = ticketGroup.hostName, + hostPhoneNumber = ticketGroup.hostPhoneNumber, + onClickCopyPlace = { + clipboardManager.setText(AnnotatedString(ticketGroup.streetAddress)) + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) { + snackbarHostController.showMessage(copySuccessMessage) + } + }, + onClickNavigateToShowDetail = { + navigateToShowDetail(ticketGroup.showId) + } + ) + } } + // 티켓 좌상단 꼭지점 그라데이션 + Box( + modifier = Modifier + .align(Alignment.TopStart) + .size(105.dp) + .alpha(.45f) + .background( + Brush.linearGradient( + colors = listOf(White, White.copy(alpha = 0f)), + end = Offset(50f, 50f), + ) + ), + ) } Spacer(modifier = Modifier.size(20.dp)) @@ -371,7 +370,7 @@ private fun TicketDetailScreen( ) { Text( modifier = Modifier - .padding(top = 20.dp, bottom = 60.dp) + .padding(top = 20.dp) .align(Alignment.CenterHorizontally) .clickable { showEnterCodeDialog = true }, text = stringResource(R.string.input_enter_code_button), @@ -384,7 +383,7 @@ private fun TicketDetailScreen( if (uiState.isRefundableGift) { Text( modifier = Modifier - .padding(top = 20.dp, bottom = 60.dp) + .padding(top = 20.dp) .align(Alignment.CenterHorizontally) .clickable { showRefundGiftTicket = true }, text = stringResource(R.string.cancel_registered_gift_button), From e08ce085836511dd30aa08e0cec43035fdd4f5d1 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 23:22:30 +0900 Subject: [PATCH 72/90] =?UTF-8?q?feature/Boolti-343=20=ED=8B=B0=EC=BC=93?= =?UTF-8?q?=20=EC=83=81=EC=84=B8=20=ED=99=94=EB=A9=B4=20=EA=B0=84=EA=B2=A9?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../screen/ticket/detail/TicketDetailScreen.kt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt index dd23540f7..542aaa859 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt @@ -217,8 +217,9 @@ private fun TicketDetailScreen( ) { innerPadding -> Box( modifier = Modifier + .fillMaxSize() .padding(innerPadding) - .nestedScroll(pullToRefreshState.nestedScrollConnection) + .nestedScroll(pullToRefreshState.nestedScrollConnection), ) { PullToRefreshContainer( modifier = Modifier @@ -229,8 +230,9 @@ private fun TicketDetailScreen( Column( modifier = Modifier .padding(horizontal = 29.dp) - .padding(bottom = 20.dp) .verticalScroll(scrollState), + verticalArrangement = Arrangement.spacedBy(20.dp), + horizontalAlignment = Alignment.CenterHorizontally, ) { val ticketShape = TicketShape( width = contentWidth, @@ -241,11 +243,11 @@ private fun TicketDetailScreen( ) Box( modifier = Modifier + .padding(top = 16.dp) .onGloballyPositioned { coordinates -> contentWidth = coordinates.size.width.toFloat() ticketSectionHeight = coordinates.size.height.toFloat() } - .padding(top = 16.dp) .clip(ticketShape) .border( width = 1.dp, @@ -361,7 +363,6 @@ private fun TicketDetailScreen( ) } - Spacer(modifier = Modifier.size(20.dp)) RefundPolicySection(uiState.refundPolicy) if ( @@ -370,8 +371,6 @@ private fun TicketDetailScreen( ) { Text( modifier = Modifier - .padding(top = 20.dp) - .align(Alignment.CenterHorizontally) .clickable { showEnterCodeDialog = true }, text = stringResource(R.string.input_enter_code_button), style = MaterialTheme.typography.bodySmall, @@ -383,8 +382,6 @@ private fun TicketDetailScreen( if (uiState.isRefundableGift) { Text( modifier = Modifier - .padding(top = 20.dp) - .align(Alignment.CenterHorizontally) .clickable { showRefundGiftTicket = true }, text = stringResource(R.string.cancel_registered_gift_button), style = MaterialTheme.typography.bodySmall, @@ -392,6 +389,8 @@ private fun TicketDetailScreen( textDecoration = TextDecoration.Underline, ) } + + Spacer(Modifier.size(20.dp)) } } } From c5050231750dcd6b2119229a51df0fbbd8c76bec Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 23:33:33 +0900 Subject: [PATCH 73/90] =?UTF-8?q?feature/Boolti-347=20=ED=99=88=EC=97=90?= =?UTF-8?q?=EC=84=9C=EB=8F=84=20=EC=9D=B8=EC=95=B1=20=EA=B3=B5=EC=97=B0=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/home/HomeScreen.kt | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt index 42f5dc38f..8f5c4978f 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt @@ -2,7 +2,6 @@ package com.nexters.boolti.presentation.screen.home import android.content.Intent import android.net.Uri -import android.widget.Toast import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.compose.foundation.layout.Column @@ -25,7 +24,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -36,7 +34,6 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.navDeepLink -import com.nexters.boolti.presentation.BuildConfig import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.extension.requireActivity import com.nexters.boolti.presentation.screen.LocalSnackbarController @@ -69,9 +66,6 @@ fun HomeScreen( val currentDestination = navBackStackEntry?.destination?.route ?: Destination.Show.route val loggedIn by viewModel.loggedIn.collectAsStateWithLifecycle() - val domain = BuildConfig.DOMAIN - val url = "https://${domain}/show/add" - val uriHandler = LocalUriHandler.current val context = LocalContext.current val giftRegistrationMessage = stringResource(id = R.string.gift_successfully_registered) @@ -141,10 +135,7 @@ fun HomeScreen( modifier = modifier.padding(innerPadding), onClickShowItem = onClickShowItem, navigateToBusiness = navigateToBusiness, - navigateToShowRegistration = { - uriHandler.openUri(url) - Toast.makeText(context, "공연 등록을 위해 웹으로 이동합니다", Toast.LENGTH_LONG).show() - } // navigateToShowRegistration, // TODO 추후 인앱 공연 등록 반영 시 주석 해제 + navigateToShowRegistration = navigateToShowRegistration, ) } composable( From 6c4fa081a4d86f7df463a461a58bb516f1453087 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Wed, 1 Jan 2025 11:59:55 +0900 Subject: [PATCH 74/90] =?UTF-8?q?feature/Boolti-347=20=EB=B8=8C=EB=A6=BF?= =?UTF-8?q?=EC=A7=80=EC=97=90=20=EC=8A=A4=EB=82=B5=EB=B0=94=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ShowRegistrationScreen.kt | 8 +++++++ .../presentation/util/SnackbarController.kt | 4 +++- .../util/bridge/BridgeCallbackHandler.kt | 2 ++ .../presentation/util/bridge/BridgeManager.kt | 21 +++++++++++++++++++ .../presentation/util/bridge/CommandType.kt | 1 + 5 files changed, 35 insertions(+), 1 deletion(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt index 4d021a2b7..964b3c396 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -36,6 +37,7 @@ import com.nexters.boolti.presentation.component.BTDialog import com.nexters.boolti.presentation.component.BtBackAppBar import com.nexters.boolti.presentation.component.BtCircularProgressIndicator import com.nexters.boolti.presentation.component.BtWebView +import com.nexters.boolti.presentation.screen.LocalSnackbarController import com.nexters.boolti.presentation.util.bridge.BridgeCallbackHandler import com.nexters.boolti.presentation.util.bridge.BridgeManager import com.nexters.boolti.presentation.util.bridge.NavigateOption @@ -68,6 +70,8 @@ fun ShowRegistrationScreen( var webviewProgress by remember { mutableIntStateOf(0) } val loading by remember { derivedStateOf { webviewProgress < 100 } } + val snackbarHostState = LocalSnackbarController.current + LaunchedEffect(webView != null) { webView?.setBridgeManager( BridgeManager( @@ -87,6 +91,10 @@ fun ShowRegistrationScreen( } } } + + override fun showSnackbar(message: String, duration: SnackbarDuration) { + snackbarHostState.showMessage(message = message, duration = duration) + } }, scope = scope, ) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/SnackbarController.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/SnackbarController.kt index 3e388b7c5..9666b017d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/util/SnackbarController.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/SnackbarController.kt @@ -1,5 +1,6 @@ package com.nexters.boolti.presentation.util +import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHostState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob @@ -12,10 +13,11 @@ class SnackbarController( fun showMessage( message: String, dismissPrevious: Boolean = true, + duration: SnackbarDuration = SnackbarDuration.Short, ) { coroutineScope.launch { if (dismissPrevious) snackbarHostState.currentSnackbarData?.dismiss() - snackbarHostState.showSnackbar(message) + snackbarHostState.showSnackbar(message, duration = duration) } } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt index 69cc25f15..0d57ecac6 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt @@ -1,5 +1,6 @@ package com.nexters.boolti.presentation.util.bridge +import androidx.compose.material3.SnackbarDuration import kotlinx.serialization.Serializable /** @@ -10,6 +11,7 @@ import kotlinx.serialization.Serializable interface BridgeCallbackHandler { suspend fun fetchToken(): TokenDto fun navigateTo(route: String, navigateOption: NavigateOption = NavigateOption.PUSH) + fun showSnackbar(message: String, duration: SnackbarDuration = SnackbarDuration.Short) } enum class NavigateOption { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt index 7e67b9d8e..b33d50864 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt @@ -1,5 +1,6 @@ package com.nexters.boolti.presentation.util.bridge +import androidx.compose.material3.SnackbarDuration import com.nexters.boolti.presentation.screen.MainDestination import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -59,6 +60,26 @@ class BridgeManager( callbackToWeb(data, null) } + CommandType.SHOW_TOAST -> { + data.data?.jsonObject?.let { + val message = it["message"]?.toString() ?: run { + Timber.tag("bridge").d("토스트 메시지 출력 실패: message 없음") + callbackToWeb(data, null) + return@let + } + val duration = it["duration"]?.toString()?.let { durationStr -> + if (durationStr.equals("long", true)) { + SnackbarDuration.Long + } else { + SnackbarDuration.Short + } + } ?: SnackbarDuration.Short + + callbackHandler.showSnackbar(message, duration) + } + callbackToWeb(data, null) + } + else -> callbackToWeb(data, null) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/CommandType.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/CommandType.kt index ed728aeb3..ca2e88aa9 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/CommandType.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/CommandType.kt @@ -7,6 +7,7 @@ enum class CommandType { NAVIGATE_TO_SHOW_DETAIL, NAVIGATE_BACK, REQUEST_TOKEN, + SHOW_TOAST, UNKNOWN; companion object { From 37fef6e5178352cb2797659ec3c2a5b967870e70 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Wed, 1 Jan 2025 17:48:05 +0900 Subject: [PATCH 75/90] =?UTF-8?q?feature/Boolti-347=20=EB=B8=8C=EB=A6=BF?= =?UTF-8?q?=EC=A7=80=20=EC=BD=9C=EB=B0=B1=EC=97=90=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=B0=98=EC=98=81=20=EB=88=84?= =?UTF-8?q?=EB=9D=BD=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/util/bridge/BridgeManager.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt index b33d50864..570b841e4 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt @@ -57,14 +57,14 @@ class BridgeManager( } } } ?: Timber.tag("bridge").d("공연 상세 화면으로 이동 실패: showId 없음") - callbackToWeb(data, null) + callbackToWeb(data) } CommandType.SHOW_TOAST -> { data.data?.jsonObject?.let { val message = it["message"]?.toString() ?: run { Timber.tag("bridge").d("토스트 메시지 출력 실패: message 없음") - callbackToWeb(data, null) + callbackToWeb(data) return@let } val duration = it["duration"]?.toString()?.let { durationStr -> @@ -77,10 +77,10 @@ class BridgeManager( callbackHandler.showSnackbar(message, duration) } - callbackToWeb(data, null) + callbackToWeb(data) } - else -> callbackToWeb(data, null) + else -> callbackToWeb(data) } } @@ -107,10 +107,10 @@ class BridgeManager( */ private fun callbackToWeb( originData: BridgeDto, - responseData: JsonElement? + responseData: JsonElement? = null, ) { val responseDto = originData.copy( - data = responseData, + data = responseData ?: originData.data, timestamp = System.currentTimeMillis(), ) val json = json.encodeToString(responseDto) From 18ad02fbc95ea9dac7d757c9b592e2e9b149efd3 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Wed, 1 Jan 2025 18:15:18 +0900 Subject: [PATCH 76/90] =?UTF-8?q?feature/Boolti-347=20=EB=A8=B8=EC=A7=80?= =?UTF-8?q?=20=EC=B6=A9=EB=8F=8C=20=ED=95=B4=EA=B2=B0=20=EB=B0=8F=20?= =?UTF-8?q?=EB=82=B4=EB=B9=84=EA=B2=8C=EC=9D=B4=EC=85=98=202.8.x=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../screen/showregistration/ShowRegistrationNavigation.kt | 1 + .../screen/showregistration/ShowRegistrationScreen.kt | 4 ++-- .../presentation/util/bridge/BridgeCallbackHandler.kt | 2 +- .../boolti/presentation/util/bridge/BridgeManager.kt | 6 +++--- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationNavigation.kt index 62d9a5dd2..a3809475e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationNavigation.kt @@ -17,6 +17,7 @@ fun NavGraphBuilder.addShowRegistration( ShowRegistrationScreen( modifier = modifier, onClickBack = navController::popBackStack, + navigateTo = navController::navigate, navigateToHome = navController::navigateToHome, ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt index 964b3c396..be4f0713f 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showregistration/ShowRegistrationScreen.kt @@ -50,7 +50,7 @@ import timber.log.Timber fun ShowRegistrationScreen( modifier: Modifier = Modifier, onClickBack: () -> Unit, - navigateTo: (route: String) -> Unit, + navigateTo: (route: Any) -> Unit, navigateToHome: () -> Unit, viewModel: ShowRegistrationViewModel = hiltViewModel(), ) { @@ -81,7 +81,7 @@ fun ShowRegistrationScreen( return TokenDto(token = accessToken.token) } - override fun navigateTo(route: String, navigateOption: NavigateOption) { + override fun navigate(route: T, navigateOption: NavigateOption) { when (navigateOption) { NavigateOption.PUSH -> navigateTo(route) NavigateOption.HOME -> navigateToHome() diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt index 0d57ecac6..e1f5021f6 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeCallbackHandler.kt @@ -10,7 +10,7 @@ import kotlinx.serialization.Serializable */ interface BridgeCallbackHandler { suspend fun fetchToken(): TokenDto - fun navigateTo(route: String, navigateOption: NavigateOption = NavigateOption.PUSH) + fun navigate(route: T, navigateOption: NavigateOption = NavigateOption.PUSH) fun showSnackbar(message: String, duration: SnackbarDuration = SnackbarDuration.Short) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt index 570b841e4..a6f6d9e18 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt @@ -1,7 +1,7 @@ package com.nexters.boolti.presentation.util.bridge import androidx.compose.material3.SnackbarDuration -import com.nexters.boolti.presentation.screen.MainDestination +import com.nexters.boolti.presentation.screen.navigation.ShowRoute import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -50,8 +50,8 @@ class BridgeManager( scope.launch { withContext(Dispatchers.Main) { Timber.tag("bridge").d("공연 상세 화면으로 이동 $showId") - callbackHandler.navigateTo( - route = MainDestination.ShowDetail.createRoute(showId), + callbackHandler.navigate( + route = ShowRoute.ShowRoot(showId), navigateOption = NavigateOption.CLOSE_AND_OPEN, ) } From 368d4dfcbc63f61d20883f0595033d7251fb62e1 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sat, 11 Jan 2025 20:44:22 +0900 Subject: [PATCH 77/90] =?UTF-8?q?Boolti-343=20=EC=84=A0=EB=AC=BC=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C,=20=ED=8B=B0=EC=BC=93=EC=84=A0=ED=83=9D=20?= =?UTF-8?q?=EB=B0=94=ED=85=80=EC=8B=9C=ED=8A=B8=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../screen/giftcomplete/GiftCompleteScreen.kt | 29 ++++++++++--------- .../ticketing/ChooseTicketBottomSheet.kt | 4 +-- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteScreen.kt index e32c79441..562c0e28a 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/giftcomplete/GiftCompleteScreen.kt @@ -73,7 +73,7 @@ fun GiftCompleteScreen( onClickClose: () -> Unit, navigateToReservation: (reservation: ReservationDetail) -> Unit, viewModel: GiftCompleteViewModel = hiltViewModel(), -) { +) { val reservationState by viewModel.reservation.collectAsStateWithLifecycle() val reservation = reservationState @@ -110,11 +110,14 @@ fun GiftCompleteScreen( PaymentToolbar(onClickHome = onClickHome, onClickClose = onClickClose) }, ) { innerPadding -> - Box { + Box( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding) + .padding(horizontal = marginHorizontal), + ) { Column( modifier = Modifier - .padding(innerPadding) - .padding(horizontal = marginHorizontal) .verticalScroll(rememberScrollState()) ) { val month = reservation.salesEndDateTime.month.value @@ -185,15 +188,15 @@ fun GiftCompleteScreen( ShowInformation( reservation = reservation ) - SecondaryButton( - modifier = Modifier - .fillMaxWidth() - .padding(top = 16.dp) - .padding(vertical = 8.dp), - label = stringResource(R.string.show_reservation), - ) { - navigateToReservation(reservation) - } + } + SecondaryButton( + modifier = Modifier + .align(Alignment.BottomCenter) + .fillMaxWidth() + .padding(top = 16.dp, bottom = 20.dp), + label = stringResource(R.string.show_reservation), + ) { + navigateToReservation(reservation) } } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/ChooseTicketBottomSheet.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/ChooseTicketBottomSheet.kt index 1612998a0..356b6df9b 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/ChooseTicketBottomSheet.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/ChooseTicketBottomSheet.kt @@ -137,7 +137,7 @@ private fun ChooseTicketBottomSheetContent1( ) { val listState = rememberLazyListState() Column( - modifier = Modifier.padding(bottom = 20.dp), + modifier = Modifier.padding(bottom = 32.dp), ) { Text( text = stringResource(id = R.string.choose_ticket_bottomsheet_title), @@ -250,7 +250,7 @@ private fun ChooseTicketBottomSheetContent2( label = stringResource(buttonTextId), modifier = Modifier .fillMaxWidth() - .padding(start = 24.dp, end = 24.dp, top = 8.dp, bottom = 24.dp) + .padding(start = 20.dp, end = 20.dp, top = 8.dp, bottom = 20.dp) .height(48.dp), onClick = { onTicketingClicked(ticket, ticketCount) }, ) From 9e177891fc7e1828f04a2204f04edfc71e46f674 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sat, 11 Jan 2025 20:44:49 +0900 Subject: [PATCH 78/90] =?UTF-8?q?Boolti-343=20=EA=B3=B5=EC=97=B0=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=EB=B2=84=ED=8A=BC=EC=9D=98=20=EA=B7=B8?= =?UTF-8?q?=EB=9D=BC=EB=8D=B0=EC=9D=B4=EC=85=98=20UI=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/GradientBackground.kt | 85 +++++++++++++++++++ .../screen/showdetail/ShowDetailButtons.kt | 29 ++----- 2 files changed, 91 insertions(+), 23 deletions(-) create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/component/GradientBackground.kt diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/component/GradientBackground.kt b/presentation/src/main/java/com/nexters/boolti/presentation/component/GradientBackground.kt new file mode 100644 index 000000000..5c3e17332 --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/component/GradientBackground.kt @@ -0,0 +1,85 @@ +package com.nexters.boolti.presentation.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +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.material3.MaterialTheme +import androidx.compose.material3.Surface +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.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.nexters.boolti.presentation.extension.toDp +import com.nexters.boolti.presentation.theme.BooltiTheme +import com.nexters.boolti.presentation.theme.marginHorizontal + +@Composable +fun TopGradientBackground( + modifier: Modifier = Modifier, + bgColor: Color = MaterialTheme.colorScheme.background, + gradientHeight: Dp = 16.dp, + content: @Composable () -> Unit, +) { + val density = LocalDensity.current + var contentWidthDp by remember { mutableStateOf(0.dp) } + + Column( + modifier = modifier, + ) { + Box( + Modifier + .width(contentWidthDp) + .height(gradientHeight) + .background( + brush = Brush.verticalGradient(listOf(Color.Transparent, bgColor)) + ) + ) + Box( + Modifier + .onGloballyPositioned { contentWidthDp = it.size.width.toDp(density) } + .background(bgColor), + ) { + content() + } + } +} + +@Preview +@Composable +private fun TopGradientBackgroundPreview() { + BooltiTheme { + Surface( + color = Color.White, + ) { + Box( + modifier = Modifier.fillMaxSize(), + ) { + TopGradientBackground( + modifier = Modifier + .align(Alignment.BottomCenter), + ) { + SecondaryButton( + modifier = Modifier + .padding(horizontal = marginHorizontal) + .padding(bottom = 20.dp), + label = "예매하기", + ) { } + } + } + } + } +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailButtons.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailButtons.kt index 68ff50c65..27d604315 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailButtons.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailButtons.kt @@ -2,18 +2,13 @@ package com.nexters.boolti.presentation.screen.showdetail import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column 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.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -23,6 +18,7 @@ import com.nexters.boolti.domain.model.ShowState import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.component.MainButton import com.nexters.boolti.presentation.component.MainButtonDefaults +import com.nexters.boolti.presentation.component.TopGradientBackground import com.nexters.boolti.presentation.extension.asString import com.nexters.boolti.presentation.extension.toDp import com.nexters.boolti.presentation.theme.BooltiTheme @@ -39,24 +35,11 @@ fun ShowDetailButtons( onHeightChanged: (height: Dp) -> Unit = {}, ) { val density = LocalDensity.current - Column( - modifier = Modifier.onSizeChanged { - onHeightChanged(it.height.toDp(density)) - }, + TopGradientBackground( + modifier = Modifier.onGloballyPositioned { coord -> + onHeightChanged(coord.size.height.toDp(density)) + } ) { - Spacer( - modifier = Modifier - .fillMaxWidth() - .height(16.dp) - .background( - brush = Brush.verticalGradient( - colors = listOf( - Color.Transparent, - MaterialTheme.colorScheme.background, - ) - ) - ) - ) Row( modifier = Modifier .fillMaxWidth() From 53a6e85002e110d33d6089be2faebdbefa87f365 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sat, 11 Jan 2025 20:44:59 +0900 Subject: [PATCH 79/90] =?UTF-8?q?Boolti-343=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=ED=99=94=EB=A9=B4=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/login/LoginScreen.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginScreen.kt index 776456818..de97638e8 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/login/LoginScreen.kt @@ -1,12 +1,12 @@ package com.nexters.boolti.presentation.screen.login -import android.widget.Toast import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.ClickableText @@ -25,7 +25,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.SpanStyle @@ -136,7 +135,9 @@ private fun SignUpBottomSheet( val spanOffset = Pair(term.indexOf(tag), tag.length) Column( - modifier = modifier.padding(horizontal = 24.dp), + modifier = modifier + .padding(horizontal = 24.dp) + .navigationBarsPadding(), ) { Text( modifier = Modifier @@ -169,7 +170,7 @@ private fun SignUpBottomSheet( .fillMaxWidth() .padding(top = 18.dp) .padding(vertical = 8.dp) - .padding(bottom = 34.dp), + .padding(bottom = 20.dp), label = stringResource(id = R.string.signup_with_agreement), onClick = signUp, ) From e395ff2d9ef91a82b0142b05b39dee066d50f87d Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sat, 11 Jan 2025 20:45:18 +0900 Subject: [PATCH 80/90] =?UTF-8?q?Boolti-343=20=EC=98=88=EB=A7=A4=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20stateless=20=EC=BB=B4=ED=8F=AC=EC=A0=80?= =?UTF-8?q?=EB=B8=94=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../screen/ticketing/TicketingScreen.kt | 97 +++++++++++++++---- 1 file changed, 80 insertions(+), 17 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingScreen.kt index b0788a50a..b80d1bee0 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingScreen.kt @@ -91,6 +91,8 @@ import com.nexters.boolti.tosspayments.TossPaymentWidgetActivity import com.nexters.boolti.tosspayments.TossPaymentWidgetActivity.Companion.RESULT_FAIL import com.nexters.boolti.tosspayments.TossPaymentWidgetActivity.Companion.RESULT_SOLD_OUT import com.nexters.boolti.tosspayments.TossPaymentWidgetActivity.Companion.RESULT_SUCCESS +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow @Composable fun TicketingScreen( @@ -99,10 +101,52 @@ fun TicketingScreen( onBackClicked: () -> Unit = {}, onReserved: (reservationId: String, showId: String) -> Unit, navigateToBusiness: () -> Unit, +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + TicketingScreen( + showId = viewModel.showId, + salesTicketTypeId = viewModel.salesTicketTypeId, + uiState = uiState, + event = viewModel.event, + onBackClicked = onBackClicked, + onReserved = onReserved, + navigateToBusiness = navigateToBusiness, + onChangeReservationName = viewModel::setReservationName, + onChangeReservationPhoneNumber = viewModel::setReservationPhoneNumber, + onChangeDepositorName = viewModel::setDepositorName, + onChangeDepositorPhoneNumber = viewModel::setDepositorPhoneNumber, + onToggleIsSameContactInfo = viewModel::toggleIsSameContactInfo, + onClickCheckInviteCode = viewModel::checkInviteCode, + onInviteCodeChanged = viewModel::setInviteCode, + onToggleAgreement = viewModel::toggleAgreement, + onClickReservation = viewModel::reservation, + modifier = modifier, + ) +} + +@Composable +private fun TicketingScreen( + showId: String, + salesTicketTypeId: String, + uiState: TicketingState, + event: Flow, + onBackClicked: () -> Unit = {}, + onReserved: (reservationId: String, showId: String) -> Unit, + navigateToBusiness: () -> Unit, + onChangeReservationName: (String) -> Unit, + onChangeReservationPhoneNumber: (String) -> Unit, + onChangeDepositorName: (String) -> Unit, + onChangeDepositorPhoneNumber: (String) -> Unit, + onToggleIsSameContactInfo: () -> Unit, + onClickCheckInviteCode: () -> Unit, + onInviteCodeChanged: (String) -> Unit, + onToggleAgreement: () -> Unit, + onClickReservation: () -> Unit, + modifier: Modifier = Modifier, ) { val scrollState = rememberScrollState() val snackbarHostState = remember { SnackbarHostState() } - val uiState by viewModel.uiState.collectAsStateWithLifecycle() var showConfirmDialog by remember { mutableStateOf(false) } var showPaymentFailureDialog by remember { mutableStateOf(false) } var showTicketSoldOutDialog by remember { mutableStateOf(false) } @@ -117,7 +161,7 @@ fun TicketingScreen( val reservationId = intent.getStringExtra("reservationId") ?: return@rememberLauncherForActivityResult - onReserved(reservationId, viewModel.showId) + onReserved(reservationId, showId) } RESULT_SOLD_OUT -> showTicketSoldOutDialog = true @@ -125,8 +169,8 @@ fun TicketingScreen( } } - LaunchedEffect(viewModel.event) { - viewModel.event.collect { + LaunchedEffect(event) { + event.collect { when (it) { is TicketingEvent.TicketingSuccess -> { showConfirmDialog = false @@ -141,11 +185,11 @@ fun TicketingScreen( clientKey = BuildConfig.TOSS_CLIENT_KEY, customerKey = "user-${it.userId}", orderId = it.orderId, - orderName = "${viewModel.showId}/${uiState.ticketName}/${uiState.ticketCount}/Android", + orderName = "${showId}/${uiState.ticketName}/${uiState.ticketCount}/Android", currency = Currency.KRW.name, countryCode = "KR", - showId = viewModel.showId, - salesTicketTypeId = viewModel.salesTicketTypeId, + showId = showId, + salesTicketTypeId = salesTicketTypeId, ticketCount = uiState.ticketCount, reservationName = uiState.reservationName, reservationPhoneNumber = uiState.reservationContact, @@ -199,8 +243,8 @@ fun TicketingScreen( name = uiState.reservationName, phoneNumber = uiState.reservationContact, isSameContactInfo = uiState.isSameContactInfo, - onNameChanged = viewModel::setReservationName, - onPhoneNumberChanged = viewModel::setReservationPhoneNumber, + onNameChanged = onChangeReservationName, + onPhoneNumberChanged = onChangeReservationPhoneNumber, ) // 입금자 정보 @@ -209,9 +253,9 @@ fun TicketingScreen( name = uiState.depositorName, phoneNumber = uiState.depositorContact, isSameContactInfo = uiState.isSameContactInfo, - onClickSameContact = viewModel::toggleIsSameContactInfo, - onNameChanged = viewModel::setDepositorName, - onPhoneNumberChanged = viewModel::setDepositorPhoneNumber, + onClickSameContact = onToggleIsSameContactInfo, + onNameChanged = onChangeDepositorName, + onPhoneNumberChanged = onChangeDepositorPhoneNumber, ) } @@ -229,8 +273,8 @@ fun TicketingScreen( InviteCodeSection( uiState.inviteCode, uiState.inviteCodeStatus, - onClickCheckInviteCode = viewModel::checkInviteCode, - onInviteCodeChanged = viewModel::setInviteCode, + onClickCheckInviteCode = onClickCheckInviteCode, + onInviteCodeChanged = onInviteCodeChanged, ) } @@ -240,7 +284,7 @@ fun TicketingScreen( OrderAgreementSection( totalAgreed = uiState.orderAgreed, agreement = uiState.orderAgreement, - onClickTotalAgree = viewModel::toggleAgreement, + onClickTotalAgree = onToggleAgreement, onClickShow = { when (it) { 0 -> policyPageUrl = "https://boolti.in/site-policy/privacy" @@ -309,7 +353,7 @@ fun TicketingScreen( ticketName = uiState.ticketName, ticketCount = uiState.ticketCount, totalPrice = uiState.totalPrice, - onClick = viewModel::reservation, + onClick = onClickReservation, onDismiss = { showConfirmDialog = false }, ) } @@ -789,11 +833,30 @@ private fun SectionTicketInfo(label: String, value: String, marginTop: Dp = 16.d @Preview @Composable private fun TicketingDetailScreenPreview() { + val uiState = TicketingState( + showName = "2024 TOGETHER LUCKY CLUB", + ticketName = "일반 티켓 B", + ticketCount = 2, + totalPrice = 5000, + ) BooltiTheme { Surface { TicketingScreen( + showId = "", + salesTicketTypeId = "", + uiState = uiState, + event = emptyFlow(), onReserved = { _, _ -> }, - navigateToBusiness = {} + navigateToBusiness = {}, + onChangeReservationName = {}, + onChangeReservationPhoneNumber = {}, + onChangeDepositorName = {}, + onChangeDepositorPhoneNumber = {}, + onToggleIsSameContactInfo = {}, + onToggleAgreement = {}, + onClickCheckInviteCode = {}, + onInviteCodeChanged = {}, + onClickReservation = {}, ) } } From 06d9515fbe9b20b7f05fb79899c59c45f0443c57 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sat, 11 Jan 2025 20:50:43 +0900 Subject: [PATCH 81/90] =?UTF-8?q?Boolti-343=20=EA=B2=B0=EC=A0=9C=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/GradientBackground.kt | 5 +++- .../screen/ticketing/TicketingScreen.kt | 25 +++++-------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/component/GradientBackground.kt b/presentation/src/main/java/com/nexters/boolti/presentation/component/GradientBackground.kt index 5c3e17332..f2ebe297e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/component/GradientBackground.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/component/GradientBackground.kt @@ -32,13 +32,16 @@ fun TopGradientBackground( modifier: Modifier = Modifier, bgColor: Color = MaterialTheme.colorScheme.background, gradientHeight: Dp = 16.dp, + onHeightChanged: (Dp) -> Unit = {}, content: @Composable () -> Unit, ) { val density = LocalDensity.current var contentWidthDp by remember { mutableStateOf(0.dp) } Column( - modifier = modifier, + modifier = modifier.onGloballyPositioned { + onHeightChanged(it.size.height.toDp(density)) + }, ) { Box( Modifier diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingScreen.kt index b80d1bee0..9be2d0ffc 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticketing/TicketingScreen.kt @@ -44,8 +44,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -72,6 +70,7 @@ import com.nexters.boolti.presentation.component.MainButton import com.nexters.boolti.presentation.component.PolicyBottomSheet import com.nexters.boolti.presentation.component.ShowItem import com.nexters.boolti.presentation.component.ToastSnackbarHost +import com.nexters.boolti.presentation.component.TopGradientBackground import com.nexters.boolti.presentation.extension.filterToPhoneNumber import com.nexters.boolti.presentation.theme.BooltiTheme import com.nexters.boolti.presentation.theme.Error @@ -152,6 +151,7 @@ private fun TicketingScreen( var showTicketSoldOutDialog by remember { mutableStateOf(false) } var policyPageUrl: String? by remember { mutableStateOf(null) } val context = LocalContext.current + var bottomButtonHeight by remember { mutableStateOf(0.dp) } val paymentLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> @@ -307,26 +307,13 @@ private fun TicketingScreen( modifier = Modifier.fillMaxWidth(), onClick = navigateToBusiness ) - Spacer(modifier = Modifier.height(120.dp)) + Spacer(modifier = Modifier.height(bottomButtonHeight)) } - Column( - modifier = Modifier.align(Alignment.BottomCenter) + TopGradientBackground( + modifier = Modifier.align(Alignment.BottomCenter), + onHeightChanged = { bottomButtonHeight = it }, ) { - Box( - modifier = Modifier - .fillMaxWidth() - .height(16.dp) - .background( - brush = Brush.verticalGradient( - colors = listOf( - MaterialTheme.colorScheme.background.copy(alpha = 0F), - MaterialTheme.colorScheme.background, - ) - ), - shape = RectangleShape, - ) - ) MainButton( modifier = Modifier .fillMaxWidth() From 63369ecaa8298d8aa4b1ade40d6725579b9497d0 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sat, 11 Jan 2025 20:54:17 +0900 Subject: [PATCH 82/90] =?UTF-8?q?Boolti-343=20=EC=84=A0=EB=AC=BC=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/screen/gift/GiftScreen.kt | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftScreen.kt index 7f1975aff..2183d8522 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/gift/GiftScreen.kt @@ -26,8 +26,6 @@ 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.Brush -import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -45,6 +43,7 @@ import com.nexters.boolti.presentation.component.BusinessInformation import com.nexters.boolti.presentation.component.MainButton import com.nexters.boolti.presentation.component.PolicyBottomSheet import com.nexters.boolti.presentation.component.ShowItem +import com.nexters.boolti.presentation.component.TopGradientBackground import com.nexters.boolti.presentation.screen.ticketing.InputRow import com.nexters.boolti.presentation.screen.ticketing.OrderAgreementSection import com.nexters.boolti.presentation.screen.ticketing.PaymentFailureDialog @@ -73,6 +72,7 @@ fun GiftScreen( var showTicketSoldOutDialog by remember { mutableStateOf(false) } var showPaymentFailureDialog by remember { mutableStateOf(false) } var policyPageUrl: String? by remember { mutableStateOf(null) } + var bottomButtonHeight by remember { mutableStateOf(0.dp) } val paymentLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> @@ -231,26 +231,13 @@ fun GiftScreen( modifier = Modifier.fillMaxWidth(), onClick = navigateToBusiness ) - Spacer(modifier = Modifier.height(120.dp)) + Spacer(modifier = Modifier.height(bottomButtonHeight)) } - Column( - modifier = Modifier.align(Alignment.BottomCenter) + TopGradientBackground( + modifier = Modifier.align(Alignment.BottomCenter), + onHeightChanged = { bottomButtonHeight = it }, ) { - Box( - modifier = Modifier - .fillMaxWidth() - .height(16.dp) - .background( - brush = Brush.verticalGradient( - colors = listOf( - MaterialTheme.colorScheme.background.copy(alpha = 0F), - MaterialTheme.colorScheme.background, - ) - ), - shape = RectangleShape, - ) - ) MainButton( modifier = Modifier .fillMaxWidth() From abcc82a6968567c11f1f331bf86818f97bb4f78d Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sat, 11 Jan 2025 20:59:24 +0900 Subject: [PATCH 83/90] =?UTF-8?q?Boolti-343=20=EB=A7=81=ED=81=AC,=20sns=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/screen/profileedit/link/LinkEditScreen.kt | 4 +++- .../presentation/screen/profileedit/sns/SnsEditScreen.kt | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/LinkEditScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/LinkEditScreen.kt index 073c0e48a..dd99367c4 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/LinkEditScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/link/LinkEditScreen.kt @@ -36,6 +36,7 @@ import com.nexters.boolti.presentation.component.BtAppBarDefaults import com.nexters.boolti.presentation.component.MainButton import com.nexters.boolti.presentation.theme.Grey30 import com.nexters.boolti.presentation.theme.Grey90 +import com.nexters.boolti.presentation.theme.marginHorizontal @Composable fun LinkEditScreen( @@ -171,7 +172,8 @@ fun LinkEditScreen( MainButton( modifier = Modifier .align(Alignment.BottomCenter) - .padding(horizontal = 20.dp, vertical = 8.dp) + .padding(marginHorizontal) + .padding(bottom = 20.dp) .fillMaxWidth(), label = stringResource(R.string.link_remove), colors = ButtonDefaults.buttonColors( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/SnsEditScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/SnsEditScreen.kt index 1e343cd03..825bc2310 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/SnsEditScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/sns/SnsEditScreen.kt @@ -175,7 +175,9 @@ private fun SnsEditScreen( Spacer(Modifier.weight(1f)) if (isEditMode) { MainButton( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 20.dp), colors = MainButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.onSurfaceVariant, contentColor = Grey90, @@ -234,7 +236,7 @@ private fun SnsEditPreview() { BooltiTheme { SnsEditScreen( - isEditMode = false, + isEditMode = true, selectedSns = selectedSns, username = username, usernameHasError = usernameHasError, From ed8f98cf9ef2fe6b04d31b40df6b884cb36abbb9 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sun, 12 Jan 2025 00:15:11 +0900 Subject: [PATCH 84/90] =?UTF-8?q?Boolti-343=20=EC=97=AC=EB=9F=AC=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=95=98=EB=8B=A8=20=EB=A7=88?= =?UTF-8?q?=EC=A7=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/reservationdetail/ReservationDetailScreen.kt | 3 +++ .../nexters/boolti/presentation/screen/link/LinkListScreen.kt | 2 +- .../screen/perforemdshows/PerformedShowsScreen.kt | 2 +- .../screen/profileedit/profile/ProfileEditScreen.kt | 3 +++ .../nexters/boolti/presentation/screen/qr/HostedShowScreen.kt | 4 +++- .../presentation/screen/reservations/ReservationsScreen.kt | 2 +- .../presentation/screen/ticket/detail/TicketDetailScreen.kt | 3 +-- 7 files changed, 13 insertions(+), 6 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailScreen.kt index 374b10425..6a9116474 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/reservationdetail/ReservationDetailScreen.kt @@ -9,6 +9,7 @@ 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.fillMaxWidth import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding @@ -161,6 +162,8 @@ fun ReservationDetailScreen( } ) } + + Spacer(Modifier.size(32.dp)) } if (showRefundDialog) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListScreen.kt index b9d6683fb..586b38da8 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/link/LinkListScreen.kt @@ -88,7 +88,7 @@ private fun LinkListScreen( .padding(horizontal = marginHorizontal) .fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(16.dp), - contentPadding = PaddingValues(vertical = 20.dp), + contentPadding = PaddingValues(top = 20.dp, bottom = 32.dp), ) { items(links) { link -> LinkItem(link = link) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsScreen.kt index cdf0aad4d..b5a7ddc3e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/perforemdshows/PerformedShowsScreen.kt @@ -78,7 +78,7 @@ private fun PerformedShowsScreen( modifier = Modifier.padding(innerPadding), ) { LazyColumn( - contentPadding = PaddingValues(vertical = 20.dp), + contentPadding = PaddingValues(top = 20.dp, bottom = 32.dp), verticalArrangement = Arrangement.spacedBy(12.dp), ) { items(shows) { show -> diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditScreen.kt index 480c141e2..03936dc00 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditScreen.kt @@ -374,7 +374,10 @@ fun ProfileEditScreen( } } } + + Spacer(Modifier.size(32.dp)) } + if (saving) BtCircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) } if (showExitAlertDialog) { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt index 003e23b47..7000af1fe 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn @@ -50,6 +51,7 @@ fun HostedShowScreen( val uiState by viewModel.uiState.collectAsStateWithLifecycle() Scaffold( + modifier = Modifier.navigationBarsPadding(), topBar = { BtBackAppBar( title = stringResource(R.string.hostedShowsTitle), @@ -79,7 +81,7 @@ fun HostedShows( ) { LazyColumn( modifier = modifier, - contentPadding = PaddingValues(vertical = 12.dp), + contentPadding = PaddingValues(top = 12.dp, bottom = 32.dp), verticalArrangement = Arrangement.spacedBy(12.dp), ) { items(shows, key = { show -> show.id }) { show -> diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsScreen.kt index 6e376cdb5..2bc61af05 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/reservations/ReservationsScreen.kt @@ -118,7 +118,7 @@ private fun ReservationsContent( ) { LazyColumn( modifier = modifier, - contentPadding = PaddingValues(top = 20.dp) + contentPadding = PaddingValues(top = 20.dp, bottom = 32.dp), ) { items( count = reservations.size, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt index 542aaa859..097559f96 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/detail/TicketDetailScreen.kt @@ -390,7 +390,7 @@ private fun TicketDetailScreen( ) } - Spacer(Modifier.size(20.dp)) + Spacer(Modifier.size(32.dp)) } } } @@ -728,7 +728,6 @@ private fun Section( ) { Column( modifier - .padding(bottom = 12.dp) .fillMaxWidth() .border(1.dp, color = White.copy(alpha = .15f), shape = RoundedCornerShape(8.dp)) .padding(start = 20.dp, end = 20.dp, bottom = if (contentVisible) 20.dp else 0.dp) From 3ac8ae4a66438109da62131eed8de4b145f90ce8 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sun, 12 Jan 2025 00:36:09 +0900 Subject: [PATCH 85/90] =?UTF-8?q?Boolti-343=20develop=20=EB=A8=B8=EC=A7=80?= =?UTF-8?q?=20=EC=B6=A9=EB=8F=8C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nexters/boolti/presentation/screen/home/HomeScreen.kt | 5 ++++- .../nexters/boolti/presentation/screen/my/MyNavigation.kt | 3 +++ .../boolti/presentation/screen/show/ShowNavigation.kt | 3 +++ .../nexters/boolti/presentation/screen/show/ShowScreen.kt | 2 -- .../boolti/presentation/screen/ticket/TicketNavigation.kt | 4 ++++ 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt index c68fa82b2..9b17ffe14 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt @@ -120,11 +120,12 @@ fun HomeScreen( } ) { innerPadding -> NavHost( - modifier = modifier.padding(innerPadding), + modifier = modifier, navController = navController, startDestination = HomeRoute.Show, ) { showScreen( + modifier = Modifier.padding(bottom = innerPadding.calculateBottomPadding()), navigateToShowDetail = navigateToShowDetail, navigateToBusiness = navigateToBusiness, navigateToShowRegistration = { @@ -134,12 +135,14 @@ fun HomeScreen( ) ticketScreen( + modifier = Modifier.padding(innerPadding), isLoggedIn = isLoggedIn, navigateToLogin = navigateToLogin, navigateToTicketDetail = navigateToTicketDetail, ) myScreen( + modifier = Modifier.padding(innerPadding), navigateToLogin = navigateToLogin, navigateToAccountSetting = navigateToAccountSetting, navigateToReservations = navigateToReservations, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt index b04e1bf8c..1e32e59c6 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyNavigation.kt @@ -1,5 +1,6 @@ package com.nexters.boolti.presentation.screen.my +import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.nexters.boolti.presentation.screen.navigation.HomeRoute @@ -11,6 +12,7 @@ fun NavGraphBuilder.myScreen( navigateToProfile: () -> Unit, navigateToShowRegistration: () -> Unit, navigateToQrScan: () -> Unit, + modifier: Modifier = Modifier, ) { composable { MyScreen( @@ -20,6 +22,7 @@ fun NavGraphBuilder.myScreen( navigateToProfile = navigateToProfile, navigateToShowRegistration = navigateToShowRegistration, onClickQrScan = navigateToQrScan, + modifier = modifier, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt index 69e6db026..4e6171e78 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowNavigation.kt @@ -1,6 +1,7 @@ package com.nexters.boolti.presentation.screen.show import android.content.Intent +import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import androidx.navigation.navDeepLink @@ -10,6 +11,7 @@ fun NavGraphBuilder.showScreen( navigateToShowDetail: (showId: String) -> Unit, navigateToBusiness: () -> Unit, navigateToShowRegistration: () -> Unit, + modifier: Modifier = Modifier, ) { composable( deepLinks = listOf( @@ -20,6 +22,7 @@ fun NavGraphBuilder.showScreen( ) ) { ShowScreen( + modifier = modifier, onClickShowItem = navigateToShowDetail, navigateToBusiness = navigateToBusiness, navigateToShowRegistration = navigateToShowRegistration, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt index c1a3f783e..5495ae5bd 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowScreen.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -72,7 +71,6 @@ import com.nexters.boolti.presentation.theme.point1 import com.nexters.boolti.presentation.theme.point4 import com.nexters.boolti.presentation.theme.statusBarHeight -@OptIn(ExperimentalLayoutApi::class) @Composable fun ShowScreen( navigateToBusiness: () -> Unit, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt index 3fdc490e4..a70ed922a 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/ticket/TicketNavigation.kt @@ -1,6 +1,7 @@ package com.nexters.boolti.presentation.screen.ticket import android.content.Intent +import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import androidx.navigation.navDeepLink @@ -10,6 +11,7 @@ fun NavGraphBuilder.ticketScreen( isLoggedIn: Boolean?, navigateToTicketDetail: (String) -> Unit, navigateToLogin: () -> Unit, + modifier: Modifier = Modifier, ) { composable( deepLinks = listOf( @@ -21,10 +23,12 @@ fun NavGraphBuilder.ticketScreen( ) { when (isLoggedIn) { true -> TicketScreen( + modifier = modifier, onClickTicket = navigateToTicketDetail, ) false -> TicketLoginScreen( + modifier = modifier, onLoginClick = navigateToLogin ) From 23d7f5396aeffac899ae2cdf518c5b3ca4aff8bc Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sun, 12 Jan 2025 00:45:35 +0900 Subject: [PATCH 86/90] =?UTF-8?q?Boolti-347=20pr=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/util/bridge/BridgeManager.kt | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt index a6f6d9e18..89602eb65 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/util/bridge/BridgeManager.kt @@ -1,14 +1,14 @@ package com.nexters.boolti.presentation.util.bridge +import android.os.Handler +import android.os.Looper import androidx.compose.material3.SnackbarDuration import com.nexters.boolti.presentation.screen.navigation.ShowRoute import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement @@ -47,14 +47,12 @@ class BridgeManager( CommandType.NAVIGATE_TO_SHOW_DETAIL -> { data.data?.jsonObject?.get("showId")?.toString()?.let { showId -> - scope.launch { - withContext(Dispatchers.Main) { - Timber.tag("bridge").d("공연 상세 화면으로 이동 $showId") - callbackHandler.navigate( - route = ShowRoute.ShowRoot(showId), - navigateOption = NavigateOption.CLOSE_AND_OPEN, - ) - } + Handler(Looper.getMainLooper()).post { + Timber.tag("bridge").d("공연 상세 화면으로 이동 $showId") + callbackHandler.navigate( + route = ShowRoute.ShowRoot(showId), + navigateOption = NavigateOption.CLOSE_AND_OPEN, + ) } } ?: Timber.tag("bridge").d("공연 상세 화면으로 이동 실패: showId 없음") callbackToWeb(data) @@ -80,7 +78,8 @@ class BridgeManager( callbackToWeb(data) } - else -> callbackToWeb(data) + CommandType.NAVIGATE_BACK -> callbackToWeb(data) + CommandType.UNKNOWN -> callbackToWeb(data) } } From f974f44e313786d819593f809872acb71738981d Mon Sep 17 00:00:00 2001 From: mangbaam Date: Fri, 27 Dec 2024 19:20:40 +0900 Subject: [PATCH 87/90] =?UTF-8?q?Boolti-355=20style:=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=20=EC=82=AC=EC=A7=84=20=EC=98=81=EC=97=AD=20UI=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../screen/profile/ProfileScreen.kt | 271 +++++++++++------- 1 file changed, 173 insertions(+), 98 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt index 0a6750db7..70e3fa948 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt @@ -11,10 +11,10 @@ import androidx.compose.foundation.layout.FlowRow 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.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape @@ -37,17 +37,22 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import coil.compose.AsyncImage import com.nexters.boolti.domain.model.Link import com.nexters.boolti.domain.model.Sns import com.nexters.boolti.domain.model.User @@ -57,7 +62,6 @@ import com.nexters.boolti.presentation.component.BTDialog import com.nexters.boolti.presentation.component.BtAppBar import com.nexters.boolti.presentation.component.BtAppBarDefaults import com.nexters.boolti.presentation.component.ShowItem -import com.nexters.boolti.presentation.component.UserThumbnail import com.nexters.boolti.presentation.extension.toValidUrlString import com.nexters.boolti.presentation.screen.LocalSnackbarController import com.nexters.boolti.presentation.theme.BooltiTheme @@ -69,6 +73,7 @@ import com.nexters.boolti.presentation.theme.marginHorizontal import com.nexters.boolti.presentation.theme.point3 import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.emptyFlow @Composable fun ProfileScreen( @@ -142,74 +147,32 @@ fun ProfileScreen( Scaffold( modifier = modifier, - topBar = { - BtAppBar( - title = stringResource(R.string.profile_title), - colors = BtAppBarDefaults.appBarColors(containerColor = MaterialTheme.colorScheme.surface), - navigateButtons = { - BtAppBarDefaults.AppBarIconButton( - iconRes = R.drawable.ic_arrow_back, - onClick = onClickBack, - ) - }, - actionButtons = { - if (isMine) { - BtAppBarDefaults.AppBarTextButton( - label = stringResource(R.string.edit), - onClick = navigateToProfileEdit, - ) - } else { - BtAppBarDefaults.AppBarIconButton( - iconRes = R.drawable.ic_verticle_more, - description = stringResource(R.string.description_more_menu), - onClick = { showContextMenu = true }, - ) - } - }, - ) - if (showContextMenu) { - Box( - modifier = Modifier - .fillMaxWidth() - .wrapContentSize(Alignment.TopEnd), - ) { - DropdownMenu( - modifier = Modifier.background(Grey20), - expanded = showContextMenu, - onDismissRequest = { showContextMenu = false }, - ) { - DropdownMenuItem( - text = { - Text( - text = stringResource(id = R.string.report), - color = Color.Black, - ) - }, - onClick = { - showContextMenu = false - snackbarHostState.showMessage(reportFinishedMessage) - }, - ) - } - } - } - } ) { innerPadding -> Column( modifier = modifier .verticalScroll(scrollState) .padding(innerPadding), ) { - ProfileHeader( - user = user, - onClickSns = { sns -> - try { - uriHandler.openUri(sns.url.toValidUrlString()) - } catch (e: ActivityNotFoundException) { - snackbarHostState.showMessage(invalidUrlMsg) - } - }, - ) + Box( + modifier = Modifier.fillMaxWidth() + ) { + ProfileAppBar( + onClickBack = onClickBack, + isMine = isMine, + navigateToProfileEdit = navigateToProfileEdit, + onReportFinished = { snackbarHostState.showMessage(reportFinishedMessage) }, + ) + ProfileHeader( + user = user, + onClickSns = { sns -> + try { + uriHandler.openUri(sns.url.toValidUrlString()) + } catch (e: ActivityNotFoundException) { + snackbarHostState.showMessage(invalidUrlMsg) + } + }, + ) + } if (user.link.isNotEmpty() || user.performedShow.isNotEmpty()) { Spacer(Modifier.size(8.dp)) @@ -298,6 +261,68 @@ fun ProfileScreen( } } +@Composable +private fun ProfileAppBar( + onClickBack: () -> Unit, + isMine: Boolean, + navigateToProfileEdit: () -> Unit, + onReportFinished: () -> Unit, +) { + var showContextMenu by rememberSaveable { mutableStateOf(false) } + + BtAppBar( + modifier = Modifier.zIndex(1f), + title = stringResource(R.string.profile_title), + colors = BtAppBarDefaults.appBarColors(containerColor = Color.Transparent), + navigateButtons = { + BtAppBarDefaults.AppBarIconButton( + iconRes = R.drawable.ic_arrow_back, + onClick = onClickBack, + ) + }, + actionButtons = { + if (isMine) { + BtAppBarDefaults.AppBarTextButton( + label = stringResource(R.string.edit), + onClick = navigateToProfileEdit, + ) + } else { + BtAppBarDefaults.AppBarIconButton( + iconRes = R.drawable.ic_verticle_more, + description = stringResource(R.string.description_more_menu), + onClick = { showContextMenu = true }, + ) + } + }, + ) + if (showContextMenu) { + Box( + modifier = Modifier + .fillMaxWidth() + .wrapContentSize(Alignment.TopEnd), + ) { + DropdownMenu( + modifier = Modifier.background(Grey20), + expanded = showContextMenu, + onDismissRequest = { showContextMenu = false }, + ) { + DropdownMenuItem( + text = { + Text( + text = stringResource(id = R.string.report), + color = Color.Black, + ) + }, + onClick = { + showContextMenu = false + onReportFinished() + }, + ) + } + } + } +} + @OptIn(ExperimentalLayoutApi::class) @Composable private fun ProfileHeader( @@ -306,48 +331,67 @@ private fun ProfileHeader( onClickSns: (Sns) -> Unit, ) { val shape = RoundedCornerShape( - bottomStart = 12.dp, - bottomEnd = 12.dp, + bottomStart = 20.dp, + bottomEnd = 20.dp, ) - Column( - modifier = modifier - .fillMaxWidth() - .wrapContentHeight() - .clip(shape) - .background(MaterialTheme.colorScheme.surface) - .padding(horizontal = marginHorizontal) - .padding(bottom = 32.dp), + Box( + modifier = modifier.background(MaterialTheme.colorScheme.background), ) { - UserThumbnail( - modifier = Modifier.padding(top = 40.dp), - size = 70.dp, + val defaultImage = R.drawable.ic_fallback_profile + AsyncImage( + modifier = Modifier + .fillMaxWidth() + .aspectRatio(1f), model = user.photo, + contentScale = ContentScale.Crop, + placeholder = painterResource(id = defaultImage), + fallback = painterResource(id = defaultImage), + contentDescription = stringResource(R.string.description_user_thumbnail), ) - Text( - modifier = Modifier.padding(top = 20.dp), - text = user.nickname, - style = point3, - fontWeight = FontWeight.Normal, - color = MaterialTheme.colorScheme.onSurface, - maxLines = 2, - overflow = TextOverflow.Ellipsis, + Box( + modifier = Modifier + .fillMaxWidth() + .aspectRatio(1f) + .background( + brush = Brush.verticalGradient( + listOf(Color(0x33121318), Color(0xFF121318)), + ), + ) ) - if (user.introduction.isNotBlank()) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(marginHorizontal) + .padding(top = 232.dp, bottom = 32.dp) + .clip(shape), + ) { Text( - modifier = Modifier.padding(top = 2.dp), - text = user.introduction, - color = Grey30, - style = MaterialTheme.typography.bodyLarge, + modifier = Modifier.padding(top = 20.dp), + text = user.nickname, + style = point3, + fontWeight = FontWeight.Normal, + color = MaterialTheme.colorScheme.onSurface, + maxLines = 2, + overflow = TextOverflow.Ellipsis, ) - } - if (user.sns.isNotEmpty()) { - FlowRow( - modifier = Modifier.padding(top = 16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - user.sns.forEach { sns -> SnsChip(sns) { onClickSns(sns) } } + + if (user.introduction.isNotBlank()) { + Text( + modifier = Modifier.padding(top = 2.dp), + text = user.introduction, + color = Grey30, + style = MaterialTheme.typography.bodyLarge, + ) + } + if (user.sns.isNotEmpty()) { + FlowRow( + modifier = Modifier.padding(top = 20.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + user.sns.forEach { sns -> SnsChip(sns) { onClickSns(sns) } } + } } } } @@ -472,3 +516,34 @@ private fun SectionPreview() { ) {} } } + +@Preview +@Composable +private fun ProfileScreenPreview() { + val user = User.My( + id = "", + nickname = "mangbaam", + email = "mangbaam@boolti.com", + photo = null, + userCode = "oratio", + introduction = "안녕하세요", + sns = listOf( + Sns("1", Sns.SnsType.INSTAGRAM, "hey__suun"), + Sns("1", Sns.SnsType.YOUTUBE, "tune_official"), + ), + link = listOf(), + performedShow = listOf(), + ) + BooltiTheme { + ProfileScreen( + user = user, + isMine = false, + event = emptyFlow(), + onClickBack = {}, + navigateToProfileEdit = {}, + navigateToLinks = {}, + navigateToShow = {}, + navigateToPerformedShows = {}, + ) + } +} From 040ad9f12cdd47b8a53a159cdc01293a33acec65 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 31 Dec 2024 00:26:34 +0900 Subject: [PATCH 88/90] =?UTF-8?q?Boolti-355=20style:=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=20=EC=82=AC=EC=A7=84=20=EC=98=81=EC=97=AD=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../screen/profile/ProfileScreen.kt | 122 +++++++++++------- .../res/drawable/ic_profile_placeholder.png | Bin 0 -> 81961 bytes 2 files changed, 76 insertions(+), 46 deletions(-) create mode 100644 presentation/src/main/res/drawable/ic_profile_placeholder.png diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt index 70e3fa948..33c830d07 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt @@ -1,6 +1,7 @@ package com.nexters.boolti.presentation.screen.profile import android.content.ActivityNotFoundException +import androidx.compose.animation.animateColorAsState import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -11,8 +12,9 @@ import androidx.compose.foundation.layout.FlowRow 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.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentSize @@ -32,6 +34,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -41,6 +44,9 @@ import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -62,6 +68,7 @@ import com.nexters.boolti.presentation.component.BTDialog import com.nexters.boolti.presentation.component.BtAppBar import com.nexters.boolti.presentation.component.BtAppBarDefaults import com.nexters.boolti.presentation.component.ShowItem +import com.nexters.boolti.presentation.extension.toDp import com.nexters.boolti.presentation.extension.toValidUrlString import com.nexters.boolti.presentation.screen.LocalSnackbarController import com.nexters.boolti.presentation.theme.BooltiTheme @@ -128,14 +135,20 @@ fun ProfileScreen( val invalidUrlMsg = stringResource(R.string.invalid_link) val scrollState = rememberScrollState() + val appBarBgColor by animateColorAsState( + targetValue = if (scrollState.canScrollBackward) { + MaterialTheme.colorScheme.surface + } else { + Color.Transparent + }, + label = "appBarBgColor", + ) var backDialogMessage by rememberSaveable { mutableStateOf(null) } val invalidUserMessage = stringResource(R.string.profile_invalid_user_message) val withdrawUserMessage = stringResource(R.string.profile_withdraw_user_message) val reportFinishedMessage = stringResource(R.string.report_finished) - var showContextMenu by rememberSaveable { mutableStateOf(false) } - LaunchedEffect(event) { event.collectLatest { when (it) { @@ -148,31 +161,29 @@ fun ProfileScreen( Scaffold( modifier = modifier, ) { innerPadding -> + ProfileAppBar( + onClickBack = onClickBack, + isMine = isMine, + bgColor = appBarBgColor, + navigateToProfileEdit = navigateToProfileEdit, + onReportFinished = { snackbarHostState.showMessage(reportFinishedMessage) }, + ) Column( modifier = modifier .verticalScroll(scrollState) .padding(innerPadding), ) { - Box( - modifier = Modifier.fillMaxWidth() - ) { - ProfileAppBar( - onClickBack = onClickBack, - isMine = isMine, - navigateToProfileEdit = navigateToProfileEdit, - onReportFinished = { snackbarHostState.showMessage(reportFinishedMessage) }, - ) - ProfileHeader( - user = user, - onClickSns = { sns -> - try { - uriHandler.openUri(sns.url.toValidUrlString()) - } catch (e: ActivityNotFoundException) { - snackbarHostState.showMessage(invalidUrlMsg) - } - }, - ) - } + ProfileHeader( + modifier = Modifier.fillMaxWidth(), + user = user, + onClickSns = { sns -> + try { + uriHandler.openUri(sns.url.toValidUrlString()) + } catch (e: ActivityNotFoundException) { + snackbarHostState.showMessage(invalidUrlMsg) + } + }, + ) if (user.link.isNotEmpty() || user.performedShow.isNotEmpty()) { Spacer(Modifier.size(8.dp)) @@ -265,6 +276,7 @@ fun ProfileScreen( private fun ProfileAppBar( onClickBack: () -> Unit, isMine: Boolean, + bgColor: Color, navigateToProfileEdit: () -> Unit, onReportFinished: () -> Unit, ) { @@ -273,7 +285,7 @@ private fun ProfileAppBar( BtAppBar( modifier = Modifier.zIndex(1f), title = stringResource(R.string.profile_title), - colors = BtAppBarDefaults.appBarColors(containerColor = Color.Transparent), + colors = BtAppBarDefaults.appBarColors(containerColor = bgColor), navigateButtons = { BtAppBarDefaults.AppBarIconButton( iconRes = R.drawable.ic_arrow_back, @@ -334,37 +346,55 @@ private fun ProfileHeader( bottomStart = 20.dp, bottomEnd = 20.dp, ) + var contentHeight by remember { + mutableStateOf(0.dp) + } + val screenWidth = LocalConfiguration.current.screenWidthDp.dp + val density = LocalDensity.current + val profileHeight = contentHeight.coerceAtMost(screenWidth) + + val defaultProfile = painterResource(R.drawable.ic_profile_placeholder) + Box( - modifier = modifier.background(MaterialTheme.colorScheme.background), + modifier = modifier + .clip(shape) + .background(MaterialTheme.colorScheme.surface), ) { - val defaultImage = R.drawable.ic_fallback_profile - AsyncImage( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(1f), - model = user.photo, - contentScale = ContentScale.Crop, - placeholder = painterResource(id = defaultImage), - fallback = painterResource(id = defaultImage), - contentDescription = stringResource(R.string.description_user_thumbnail), - ) Box( modifier = Modifier .fillMaxWidth() - .aspectRatio(1f) - .background( - brush = Brush.verticalGradient( - listOf(Color(0x33121318), Color(0xFF121318)), + .height(profileHeight), + ) { + AsyncImage( + modifier = Modifier.fillMaxSize(), + model = user.photo, + contentScale = ContentScale.Crop, + placeholder = defaultProfile, + fallback = defaultProfile, + contentDescription = stringResource(R.string.description_user_thumbnail), + ) + Box( + modifier = Modifier + .fillMaxSize() + .background( + brush = Brush.verticalGradient( + listOf(Color(0x33121318), Color(0xFF121318)), + ), ), - ) - ) + ) + } Column( modifier = Modifier .fillMaxWidth() - .padding(marginHorizontal) - .padding(top = 232.dp, bottom = 32.dp) - .clip(shape), + .onSizeChanged { + contentHeight = it.height.toDp(density) + } + .padding(horizontal = marginHorizontal) + .padding( + top = 188.dp, + bottom = 32.dp, + ), // TODO StatusBar 까지 확장되면 StatusBar 높이 추가되어야 함 ) { Text( modifier = Modifier.padding(top = 20.dp), @@ -526,7 +556,7 @@ private fun ProfileScreenPreview() { email = "mangbaam@boolti.com", photo = null, userCode = "oratio", - introduction = "안녕하세요", + introduction = "안녕하세요\n안녕하세요\n안녕하세요\n안녕하세요\n안녕하세요\n안녕하세요\n안녕하세요\n안녕하세요\n", sns = listOf( Sns("1", Sns.SnsType.INSTAGRAM, "hey__suun"), Sns("1", Sns.SnsType.YOUTUBE, "tune_official"), diff --git a/presentation/src/main/res/drawable/ic_profile_placeholder.png b/presentation/src/main/res/drawable/ic_profile_placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..56b31eda914e28f1a6f08d3a4c18fb5fcd59ce86 GIT binary patch literal 81961 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

  • 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 ckNi2g^i?XQ$1g2<{d9=(V~s~y4_ Date: Sun, 12 Jan 2025 01:14:59 +0900 Subject: [PATCH 89/90] =?UTF-8?q?Boolti-355=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=EC=83=81=ED=83=9C=EB=B0=94=20=ED=99=95=EC=9E=A5=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/screen/profile/ProfileScreen.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt index 33c830d07..f073b18f5 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt @@ -169,9 +169,9 @@ fun ProfileScreen( onReportFinished = { snackbarHostState.showMessage(reportFinishedMessage) }, ) Column( - modifier = modifier + modifier = Modifier .verticalScroll(scrollState) - .padding(innerPadding), + .padding(bottom = innerPadding.calculateBottomPadding()), ) { ProfileHeader( modifier = Modifier.fillMaxWidth(), From 66093b6b8d015260618acf42db09fd9602b73a11 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sun, 12 Jan 2025 02:32:26 +0900 Subject: [PATCH 90/90] =?UTF-8?q?Boolti-339=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=ED=8E=B8=EC=A7=91=20-=20=EB=A7=81=ED=81=AC,=20SNS=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle/libs.versions.toml | 2 + presentation/build.gradle.kts | 1 + .../profileedit/profile/ProfileEditScreen.kt | 128 +++++++++++++----- .../profile/ProfileEditViewModel.kt | 22 +++ .../main/res/drawable/ic_reordable_handle.xml | 27 ++++ presentation/src/main/res/values/strings.xml | 2 + 6 files changed, 151 insertions(+), 31 deletions(-) create mode 100644 presentation/src/main/res/drawable/ic_reordable_handle.xml diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 64377c36d..b11f66945 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -48,6 +48,7 @@ timber = "5.0.1" mockk = "1.13.8" tosspayments = "0.1.15" immutable = "0.3.7" +reorderable = "0.9.6" [libraries] androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity-ktx" } @@ -115,6 +116,7 @@ retrofit2-kotlinx-serialization-converter = { module = "com.jakewharton.retrofit timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" } immutable = { group = "org.jetbrains.kotlinx", name = "kotlinx-collections-immutable", version.ref = "immutable" } +reorderable = { group = "org.burnoutcrew.composereorderable", name = "reorderable", version.ref = "reorderable" } [plugins] android-application = { id = "com.android.application", version.ref = "android" } diff --git a/presentation/build.gradle.kts b/presentation/build.gradle.kts index 9e2ad64c4..5b57837d5 100644 --- a/presentation/build.gradle.kts +++ b/presentation/build.gradle.kts @@ -91,6 +91,7 @@ dependencies { implementation(libs.timber) implementation(libs.zxing.android.embedded) + implementation(libs.reorderable) androidTestImplementation(libs.bundles.android.test) androidTestImplementation(platform(libs.andoridx.compose.compose.bom)) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditScreen.kt index 480c141e2..cfb91459f 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditScreen.kt @@ -17,8 +17,11 @@ import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.text.KeyboardOptions @@ -72,6 +75,11 @@ import com.nexters.boolti.presentation.util.ObserveAsEvents import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow +import org.burnoutcrew.reorderable.ReorderableItem +import org.burnoutcrew.reorderable.ReorderableState +import org.burnoutcrew.reorderable.detectReorder +import org.burnoutcrew.reorderable.rememberReorderableLazyListState +import org.burnoutcrew.reorderable.reorderable import java.io.File import java.io.FileOutputStream import java.io.IOException @@ -147,6 +155,8 @@ fun ProfileEditScreen( onClickAddLink = { navigateToLinkEdit(null) }, onClickEditSns = { sns -> navigateToSnsEdit(sns) }, onClickEditLink = { link -> navigateToLinkEdit(link) }, + onReorderSns = viewModel::reorderSns, + onReorderLink = viewModel::reorderLink, ) } @@ -170,6 +180,8 @@ fun ProfileEditScreen( onClickAddLink: () -> Unit, onClickEditSns: (Sns) -> Unit, onClickEditLink: (Link) -> Unit, + onReorderSns: (from: Int, to: Int) -> Unit, + onReorderLink: (from: Int, to: Int) -> Unit, ) { val scrollState = rememberScrollState() val snackbarHostState = LocalSnackbarController.current @@ -334,43 +346,91 @@ fun ProfileEditScreen( ) } + val snsReorderState = rememberReorderableLazyListState( + onMove = { from, to -> + onReorderSns(from.index - 1, to.index - 1) + }, + ) Section( modifier = Modifier.padding(top = 12.dp), title = stringResource(R.string.profile_edit_sns_title), ) { - Column { - LinkAddButton( - modifier = Modifier.padding(top = 4.dp), - label = stringResource(R.string.sns_add), - onClick = onClickAddSns, - enabled = !saving, - ) - snsList.forEach { sns -> - SnsItem( - modifier = Modifier.padding(top = 12.dp), - sns = sns, - ) { if (!saving) onClickEditSns(sns) } + LazyColumn( + state = snsReorderState.listState, + modifier = Modifier + .heightIn(max = 100.dp * (snsList.size + 1)) // 대충 넉넉하게 잡은 높이 + .reorderable(snsReorderState), + ) { + item( + contentType = "SnsAddButton", + ) { + LinkAddButton( + modifier = Modifier.padding(top = 4.dp), + label = stringResource(R.string.sns_add), + onClick = onClickAddSns, + enabled = !saving, + ) + } + items( + items = snsList, + key = { it.id }, + contentType = { "SnsItem" }, + ) { sns -> + ReorderableItem( + state = snsReorderState, + key = sns.id, + ) { + SnsItem( + modifier = Modifier.padding(top = 12.dp), + sns = sns, + reorderableState = snsReorderState, + ) { if (!saving) onClickEditSns(sns) } + } } } } + val linkReorderState = rememberReorderableLazyListState( + onMove = { from, to -> + onReorderLink(from.index - 1, to.index - 1) + }, + ) Section( modifier = Modifier.padding(top = 12.dp), title = stringResource(R.string.label_links), ) { - Column { - LinkAddButton( - modifier = Modifier.padding(top = 4.dp), - label = stringResource(R.string.link_add_btn), - onClick = onClickAddLink, - enabled = !saving, - ) - links.forEach { link -> - LinkItem( - modifier = Modifier.padding(top = 12.dp), - title = link.name, - url = link.url, - ) { if (!saving) onClickEditLink(link) } + LazyColumn( + state = linkReorderState.listState, + modifier = Modifier + .heightIn(max = 100.dp * (links.size + 1)) // 대충 넉넉하게 잡은 높이 + .reorderable(linkReorderState), + ) { + item( + contentType = "LinkAddButton", + ) { + LinkAddButton( + modifier = Modifier.padding(top = 4.dp), + label = stringResource(R.string.link_add_btn), + onClick = onClickAddLink, + enabled = !saving, + ) + } + items( + items = links, + key = { it.id }, + contentType = { "LinkItem" }, + ) { link -> + ReorderableItem( + state = linkReorderState, + key = link.id, + ) { + LinkItem( + modifier = Modifier.padding(top = 12.dp), + title = link.name, + url = link.url, + reorderableState = linkReorderState, + ) { if (!saving) onClickEditLink(link) } + } } } } @@ -451,6 +511,7 @@ private fun LinkAddButton( private fun SnsItem( sns: Sns, modifier: Modifier = Modifier, + reorderableState: ReorderableState<*>, onClickEdit: () -> Unit, ) { Row( @@ -485,10 +546,13 @@ private fun SnsItem( color = Grey15, ) Icon( - modifier = Modifier.size(20.dp), - imageVector = ImageVector.vectorResource(R.drawable.ic_edit_pen), + modifier = Modifier + .padding(start = 20.dp) + .size(20.dp) + .detectReorder(reorderableState), + imageVector = ImageVector.vectorResource(R.drawable.ic_reordable_handle), tint = Grey50, - contentDescription = stringResource(R.string.link_edit), + contentDescription = stringResource(R.string.sns_reorder_description), ) } } @@ -497,6 +561,7 @@ private fun SnsItem( private fun LinkItem( title: String, url: String, + reorderableState: ReorderableState<*>, modifier: Modifier = Modifier, onClickEdit: () -> Unit, ) { @@ -530,10 +595,11 @@ private fun LinkItem( Icon( modifier = Modifier .padding(start = 20.dp) - .size(20.dp), - imageVector = ImageVector.vectorResource(R.drawable.ic_edit_pen), + .size(20.dp) + .detectReorder(reorderableState), + imageVector = ImageVector.vectorResource(R.drawable.ic_reordable_handle), tint = Grey50, - contentDescription = stringResource(R.string.link_edit), + contentDescription = stringResource(R.string.link_reorder_description), ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditViewModel.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditViewModel.kt index d7bd915b3..bf625c583 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditViewModel.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profileedit/profile/ProfileEditViewModel.kt @@ -108,6 +108,28 @@ class ProfileEditViewModel @Inject constructor( event(ProfileEditEvent.OnSnsRemoved) } + fun reorderSns(from: Int, to: Int) { + val snsList = uiState.value.snsList.toMutableList() + if (from !in snsList.indices || to !in snsList.indices) return + + _uiState.update { + it.copy( + snsList = snsList.apply { add(to, removeAt(from)) }, + ) + } + } + + fun reorderLink(from: Int, to: Int) { + val links = uiState.value.links.toMutableList() + if (from !in links.indices || to !in links.indices) return + + _uiState.update { + it.copy( + links = links.apply { add(to, removeAt(from)) }, + ) + } + } + fun completeEdits(thumbnailFile: File?) { viewModelScope.launch(recordExceptionHandler) { _uiState.update { it.copy(saving = true) } diff --git a/presentation/src/main/res/drawable/ic_reordable_handle.xml b/presentation/src/main/res/drawable/ic_reordable_handle.xml new file mode 100644 index 000000000..1ea6eac18 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_reordable_handle.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 626bf778b..17bdc172a 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -298,6 +298,7 @@ SNS를 추가했어요 SNS를 편집했어요 SNS를 삭제했어요 + SNS 순서 변경 ex) boolti_official \@을 제외한 Username을 입력해 주세요 지원하지 않는 특수문자가 포함되어 있습니다 @@ -310,6 +311,7 @@ 링크 추가 링크 추가 링크 편집 + 링크 순서 변경 링크 삭제 링크 이름 URL