diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts index 0bbaa519..70c840be 100644 --- a/core/designsystem/build.gradle.kts +++ b/core/designsystem/build.gradle.kts @@ -13,6 +13,8 @@ dependencies { implementations( projects.core.common, + libs.androidx.splash, + libs.coil.compose, libs.logger, diff --git a/core/designsystem/src/main/res/drawable/ic_dummy_splash.xml b/core/designsystem/src/main/res/drawable/ic_dummy_splash.xml new file mode 100644 index 00000000..b96b4a50 --- /dev/null +++ b/core/designsystem/src/main/res/drawable/ic_dummy_splash.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/core/designsystem/src/main/res/values/colors.xml b/core/designsystem/src/main/res/values/colors.xml index ca1931bc..2fe46cd5 100644 --- a/core/designsystem/src/main/res/values/colors.xml +++ b/core/designsystem/src/main/res/values/colors.xml @@ -7,4 +7,5 @@ #FF018786 #FF000000 #FFFFFFFF + #FF2F9647 diff --git a/core/designsystem/src/main/res/values/splash.xml b/core/designsystem/src/main/res/values/splash.xml new file mode 100644 index 00000000..83ffcd93 --- /dev/null +++ b/core/designsystem/src/main/res/values/splash.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt index f889cccd..0e94e135 100644 --- a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt +++ b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt @@ -40,7 +40,7 @@ interface ReedService { suspend fun withdraw() // User endpoints (auth required) - @PUT("api/v1/users/me/terms-agreement") + @PUT("api/v1/users/terms-agreement") suspend fun agreeTerms(@Body termsAgreementRequest: TermsAgreementRequest): TermsAgreementResponse @GET("api/v1/users/me") diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt index 8747c4ea..45b8a6d0 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt @@ -1,6 +1,7 @@ package com.ninecraft.booket.feature.detail.book 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.fillMaxSize @@ -36,8 +37,8 @@ import com.ninecraft.booket.core.designsystem.component.button.ReedButton import com.ninecraft.booket.core.designsystem.component.button.ReedButtonColorStyle import com.ninecraft.booket.core.designsystem.component.button.largeButtonStyle import com.ninecraft.booket.core.designsystem.theme.ReedTheme +import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.core.ui.component.ReedBackTopAppBar -import com.ninecraft.booket.core.ui.component.ReedFullScreen import com.ninecraft.booket.feature.detail.book.component.BookUpdateBottomSheet import com.ninecraft.booket.feature.detail.book.component.CollectedSeed import com.ninecraft.booket.feature.detail.book.component.RecordSortBottomSheet @@ -65,14 +66,13 @@ internal fun BookDetailUi( eventSink = state.eventSink, ) - ReedFullScreen(modifier = modifier) { - ReedBackTopAppBar( - title = "", - onBackClick = { - state.eventSink(BookDetailUiEvent.OnBackClick) - }, + ReedScaffold( + modifier = modifier.fillMaxSize(), + ) { innerPadding -> + BookDetailContent( + state = state, + innerPadding = innerPadding, ) - BookDetailContent(state = state) } if (state.isBookUpdateBottomSheetVisible) { @@ -122,13 +122,21 @@ internal fun BookDetailUi( @Composable internal fun BookDetailContent( state: BookDetailUiState, + innerPadding: PaddingValues, modifier: Modifier = Modifier, ) { Column( modifier = modifier .fillMaxSize() + .padding(innerPadding) .verticalScroll(rememberScrollState()), ) { + ReedBackTopAppBar( + title = "", + onBackClick = { + state.eventSink(BookDetailUiEvent.OnBackClick) + }, + ) Row( modifier = modifier .fillMaxWidth() diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailUi.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailUi.kt index d254d1e0..dfe228bb 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailUi.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailUi.kt @@ -1,8 +1,8 @@ package com.ninecraft.booket.feature.detail.record -import androidx.compose.foundation.background import androidx.compose.foundation.layout.BoxWithConstraints 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.fillMaxSize @@ -28,7 +28,7 @@ import com.ninecraft.booket.core.designsystem.component.ReedDivider import com.ninecraft.booket.core.designsystem.theme.ReedTheme import com.ninecraft.booket.core.designsystem.theme.White import com.ninecraft.booket.core.model.RecordDetailModel -import com.ninecraft.booket.core.ui.component.ReedFullScreen +import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.core.ui.component.ReedTopAppBar import com.ninecraft.booket.feature.detail.R import com.ninecraft.booket.feature.detail.record.component.QuoteBox @@ -48,26 +48,36 @@ internal fun RecordDetailUi( state = state, ) - ReedFullScreen(modifier = modifier) { - ReedTopAppBar( - title = stringResource(R.string.review_detail_title), - startIconRes = designR.drawable.ic_close, - startIconDescription = "Close Icon", - startIconOnClick = { - state.eventSink(RecordDetailUiEvent.OnCloseClicked) - }, + ReedScaffold( + modifier = modifier.fillMaxSize(), + containerColor = White, + ) { innerPadding -> + ReviewDetailContent( + state = state, + innerPadding = innerPadding, ) - ReviewDetailContent(state = state, modifier = modifier) } } @Composable -private fun ReviewDetailContent(state: RecordDetailUiState, modifier: Modifier = Modifier) { +private fun ReviewDetailContent( + state: RecordDetailUiState, + innerPadding: PaddingValues, + modifier: Modifier = Modifier, +) { Column( - modifier = Modifier + modifier = modifier .fillMaxSize() - .background(White), + .padding(innerPadding), ) { + ReedTopAppBar( + title = stringResource(R.string.review_detail_title), + startIconRes = designR.drawable.ic_close, + startIconDescription = "Close Icon", + startIconOnClick = { + state.eventSink(RecordDetailUiEvent.OnCloseClicked) + }, + ) Row( modifier = modifier .fillMaxWidth() diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts index 55ce76d4..0f7b8d5e 100644 --- a/feature/home/build.gradle.kts +++ b/feature/home/build.gradle.kts @@ -17,7 +17,5 @@ ksp { dependencies { implementations( libs.logger, - - libs.compose.system.ui.controller, ) } diff --git a/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt b/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt index a9418106..573201fb 100644 --- a/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt +++ b/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt @@ -78,6 +78,14 @@ class HomePresenter @AssistedInject constructor( is HomeUiEvent.OnBookDetailClick -> { navigator.goTo(BookDetailScreen("")) } + + is HomeUiEvent.OnTabSelected -> { + navigator.resetRoot( + newRoot = event.tab.screen, + saveState = true, + restoreState = true, + ) + } } } diff --git a/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUi.kt b/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUi.kt index 854b9617..6a768359 100644 --- a/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUi.kt +++ b/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUi.kt @@ -12,13 +12,11 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource @@ -26,15 +24,17 @@ import androidx.compose.ui.unit.dp import com.ninecraft.booket.core.designsystem.DevicePreview import com.ninecraft.booket.core.designsystem.theme.HomeBg import com.ninecraft.booket.core.designsystem.theme.ReedTheme -import com.ninecraft.booket.core.designsystem.theme.White +import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.feature.home.component.BookCard import com.ninecraft.booket.feature.home.component.EmptyBookCard import com.ninecraft.booket.feature.home.component.HomeBanner import com.ninecraft.booket.feature.home.component.HomeHeader import com.ninecraft.booket.feature.screens.HomeScreen +import com.ninecraft.booket.feature.screens.component.MainBottomBar +import com.ninecraft.booket.feature.screens.component.MainTab import com.slack.circuit.codegen.annotations.CircuitInject import dagger.hilt.android.components.ActivityRetainedComponent -import tech.thdev.compose.exteions.system.ui.controller.rememberSystemUiController +import kotlinx.collections.immutable.toImmutableList @CircuitInject(HomeScreen::class, ActivityRetainedComponent::class) @Composable @@ -42,45 +42,42 @@ internal fun HomeUi( state: HomeUiState, modifier: Modifier = Modifier, ) { - // TODO: Android 15에서 statusBar 색상 적용 안되는 문제 있음. 해결 예정. - val systemUiController = rememberSystemUiController() + HandleHomeSideEffects(state = state) - DisposableEffect(systemUiController) { - systemUiController.setStatusBarColor( - color = HomeBg, - darkIcons = true, - ) - onDispose { - systemUiController.setStatusBarColor( - color = White, - darkIcons = true, + ReedScaffold( + modifier = modifier.fillMaxSize(), + bottomBar = { + MainBottomBar( + tabs = MainTab.entries.toImmutableList(), + currentTab = MainTab.HOME, + onTabSelected = { tab -> + state.eventSink(HomeUiEvent.OnTabSelected(tab)) + }, + ) + }, + ) { innerPadding -> + Column( + modifier = Modifier + .fillMaxSize() + .background(HomeBg) + .padding(innerPadding), + ) { + HomeHeader( + onSettingsClick = { + state.eventSink(HomeUiEvent.OnSettingsClick) + }, + ) + HomeBanner( + onBookRegisterClick = { + state.eventSink(HomeUiEvent.OnBookRegisterClick) + }, + ) + HomeContent( + state = state, + modifier = Modifier, ) } } - - HandleHomeSideEffects(state = state) - - Column( - modifier = modifier - .fillMaxSize() - .background(HomeBg) - .statusBarsPadding(), - ) { - HomeHeader( - onSettingsClick = { - state.eventSink(HomeUiEvent.OnSettingsClick) - }, - ) - HomeBanner( - onBookRegisterClick = { - state.eventSink(HomeUiEvent.OnBookRegisterClick) - }, - ) - HomeContent( - state = state, - modifier = modifier, - ) - } } @Composable diff --git a/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUiState.kt b/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUiState.kt index 0961765f..b3d4fcab 100644 --- a/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUiState.kt +++ b/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomeUiState.kt @@ -2,6 +2,7 @@ package com.ninecraft.booket.feature.home import androidx.compose.runtime.Immutable import com.ninecraft.booket.core.model.RecentBookModel +import com.ninecraft.booket.feature.screens.component.MainTab import com.slack.circuit.runtime.CircuitUiEvent import com.slack.circuit.runtime.CircuitUiState import kotlinx.collections.immutable.ImmutableList @@ -27,4 +28,5 @@ sealed interface HomeUiEvent : CircuitUiEvent { data object OnBookRegisterClick : HomeUiEvent data class OnRecordButtonClick(val userBookId: String) : HomeUiEvent data object OnBookDetailClick : HomeUiEvent + data class OnTabSelected(val tab: MainTab) : HomeUiEvent } diff --git a/feature/library/build.gradle.kts b/feature/library/build.gradle.kts index 90fafaff..5f01e5ca 100644 --- a/feature/library/build.gradle.kts +++ b/feature/library/build.gradle.kts @@ -17,7 +17,5 @@ ksp { dependencies { implementations( libs.logger, - - libs.compose.system.ui.controller, ) } diff --git a/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.kt b/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.kt index d13bf479..455fc27f 100644 --- a/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.kt +++ b/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.kt @@ -131,6 +131,14 @@ class LibraryPresenter @AssistedInject constructor( is LibraryUiEvent.OnRetryClick -> { filterLibraryBooks(status = currentFilter.getApiValue(), page = currentPage, size = PAGE_SIZE) } + + is LibraryUiEvent.OnTabSelected -> { + navigator.resetRoot( + newRoot = event.tab.screen, + saveState = true, + restoreState = true, + ) + } } } diff --git a/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryUi.kt b/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryUi.kt index 00a31f8a..4185fb32 100644 --- a/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryUi.kt +++ b/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryUi.kt @@ -8,12 +8,11 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.items import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -23,18 +22,20 @@ import com.ninecraft.booket.core.designsystem.component.button.ReedButton import com.ninecraft.booket.core.designsystem.component.button.ReedButtonColorStyle import com.ninecraft.booket.core.designsystem.component.button.largeButtonStyle import com.ninecraft.booket.core.designsystem.theme.ReedTheme -import com.ninecraft.booket.core.designsystem.theme.White import com.ninecraft.booket.core.model.LibraryBookSummaryModel +import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.core.ui.component.InfinityLazyColumn import com.ninecraft.booket.core.ui.component.LoadStateFooter import com.ninecraft.booket.feature.library.component.FilterChipGroup import com.ninecraft.booket.feature.library.component.LibraryBookItem import com.ninecraft.booket.feature.library.component.LibraryHeader import com.ninecraft.booket.feature.screens.LibraryScreen +import com.ninecraft.booket.feature.screens.component.MainBottomBar +import com.ninecraft.booket.feature.screens.component.MainTab import com.slack.circuit.codegen.annotations.CircuitInject import dagger.hilt.android.components.ActivityRetainedComponent import kotlinx.collections.immutable.persistentListOf -import tech.thdev.compose.exteions.system.ui.controller.rememberSystemUiController +import kotlinx.collections.immutable.toImmutableList @CircuitInject(LibraryScreen::class, ActivityRetainedComponent::class) @Composable @@ -42,40 +43,42 @@ internal fun LibraryUi( state: LibraryUiState, modifier: Modifier = Modifier, ) { - // TODO: Android 15에서 statusBar 색상 적용 안되는 문제 있음. 해결 예정. - val systemUiController = rememberSystemUiController() - - DisposableEffect(systemUiController) { - systemUiController.setStatusBarColor( - color = White, - darkIcons = true, - ) - onDispose {} - } - HandleLibrarySideEffects( state = state, eventSink = state.eventSink, ) - Column( - modifier = modifier - .fillMaxSize() - .statusBarsPadding(), - ) { - LibraryHeader( - onSearchClick = { - state.eventSink(LibraryUiEvent.OnLibrarySearchClick) - }, - onSettingClick = { - state.eventSink(LibraryUiEvent.OnSettingsClick) - }, - ) + ReedScaffold( + modifier = modifier.fillMaxSize(), + bottomBar = { + MainBottomBar( + tabs = MainTab.entries.toImmutableList(), + currentTab = MainTab.LIBRARY, + onTabSelected = { tab -> + state.eventSink(LibraryUiEvent.OnTabSelected(tab)) + }, + ) + }, + ) { innerPadding -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), + ) { + LibraryHeader( + onSearchClick = { + state.eventSink(LibraryUiEvent.OnLibrarySearchClick) + }, + onSettingClick = { + state.eventSink(LibraryUiEvent.OnSettingsClick) + }, + ) - LibraryContent( - state = state, - modifier = modifier, - ) + LibraryContent( + state = state, + modifier = Modifier, + ) + } } } diff --git a/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryUiState.kt b/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryUiState.kt index 4d5b0497..e3e5df09 100644 --- a/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryUiState.kt +++ b/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryUiState.kt @@ -3,6 +3,7 @@ package com.ninecraft.booket.feature.library import androidx.compose.runtime.Immutable import com.ninecraft.booket.core.model.LibraryBookSummaryModel import com.ninecraft.booket.core.ui.component.FooterState +import com.ninecraft.booket.feature.screens.component.MainTab import com.slack.circuit.runtime.CircuitUiEvent import com.slack.circuit.runtime.CircuitUiState import kotlinx.collections.immutable.ImmutableList @@ -40,6 +41,7 @@ sealed interface LibraryUiEvent : CircuitUiEvent { data object OnLoadMore : LibraryUiEvent data object OnRetryClick : LibraryUiEvent data class OnFilterClick(val filterOption: LibraryFilterOption) : LibraryUiEvent + data class OnTabSelected(val tab: MainTab) : LibraryUiEvent } data class LibraryFilterChip( diff --git a/feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/LoginPresenter.kt b/feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/LoginPresenter.kt index 4b62d508..522cb880 100644 --- a/feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/LoginPresenter.kt +++ b/feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/LoginPresenter.kt @@ -7,7 +7,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import com.ninecraft.booket.core.data.api.repository.AuthRepository import com.ninecraft.booket.core.data.api.repository.UserRepository -import com.ninecraft.booket.feature.screens.BottomNavigationScreen +import com.ninecraft.booket.feature.screens.HomeScreen import com.ninecraft.booket.feature.screens.LoginScreen import com.ninecraft.booket.feature.screens.TermsAgreementScreen import com.orhanobut.logger.Logger @@ -38,7 +38,7 @@ class LoginPresenter @AssistedInject constructor( userRepository.getUserProfile() .onSuccess { userProfile -> if (userProfile.termsAgreed) { - navigator.resetRoot(BottomNavigationScreen) + navigator.resetRoot(HomeScreen) } else { navigator.resetRoot(TermsAgreementScreen) } diff --git a/feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/LoginUi.kt b/feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/LoginUi.kt index 1601705a..c55697d5 100644 --- a/feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/LoginUi.kt +++ b/feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/LoginUi.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.Text @@ -24,6 +23,7 @@ import com.ninecraft.booket.core.designsystem.component.button.ReedButtonColorSt import com.ninecraft.booket.core.designsystem.component.button.largeButtonStyle import com.ninecraft.booket.core.designsystem.theme.ReedTheme import com.ninecraft.booket.core.designsystem.theme.White +import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.feature.screens.LoginScreen import com.slack.circuit.codegen.annotations.CircuitInject import dagger.hilt.android.components.ActivityRetainedComponent @@ -39,48 +39,52 @@ internal fun LoginUi( eventSink = state.eventSink, ) - Column( - modifier = modifier - .fillMaxSize() - .background(White) - .systemBarsPadding(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - ) { - Box(modifier = modifier.fillMaxSize()) { - Text( - text = "로그인", - modifier = Modifier.align(Alignment.Center), - ) - ReedButton( - onClick = { - state.eventSink(LoginUiEvent.OnKakaoLoginButtonClick) - }, - sizeStyle = largeButtonStyle, - colorStyle = ReedButtonColorStyle.KAKAO, - modifier = Modifier - .fillMaxWidth() - .padding( - start = ReedTheme.spacing.spacing5, - end = ReedTheme.spacing.spacing5, - bottom = ReedTheme.spacing.spacing8, - ) - .align(Alignment.BottomCenter), - text = stringResource(id = R.string.kakao_login), - leadingIcon = { - Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_kakao), - contentDescription = "Kakao Icon", - tint = Color.Unspecified, - ) - }, - ) - - if (state.isLoading) { - CircularProgressIndicator( + ReedScaffold( + modifier = modifier.fillMaxSize(), + ) { innerPadding -> + Column( + modifier = Modifier + .fillMaxSize() + .background(White) + .padding(innerPadding), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + Box(modifier = Modifier.fillMaxSize()) { + Text( + text = "로그인", modifier = Modifier.align(Alignment.Center), - color = ReedTheme.colors.contentBrand, ) + ReedButton( + onClick = { + state.eventSink(LoginUiEvent.OnKakaoLoginButtonClick) + }, + sizeStyle = largeButtonStyle, + colorStyle = ReedButtonColorStyle.KAKAO, + modifier = Modifier + .fillMaxWidth() + .padding( + start = ReedTheme.spacing.spacing5, + end = ReedTheme.spacing.spacing5, + bottom = ReedTheme.spacing.spacing8, + ) + .align(Alignment.BottomCenter), + text = stringResource(id = R.string.kakao_login), + leadingIcon = { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_kakao), + contentDescription = "Kakao Icon", + tint = Color.Unspecified, + ) + }, + ) + + if (state.isLoading) { + CircularProgressIndicator( + modifier = Modifier.align(Alignment.Center), + color = ReedTheme.colors.contentBrand, + ) + } } } } diff --git a/feature/login/src/main/kotlin/com/ninecraft/booket/feature/termsagreement/TermsAgreementPresenter.kt b/feature/login/src/main/kotlin/com/ninecraft/booket/feature/termsagreement/TermsAgreementPresenter.kt index 5ffa3ffe..9d2417f2 100644 --- a/feature/login/src/main/kotlin/com/ninecraft/booket/feature/termsagreement/TermsAgreementPresenter.kt +++ b/feature/login/src/main/kotlin/com/ninecraft/booket/feature/termsagreement/TermsAgreementPresenter.kt @@ -9,7 +9,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import com.ninecraft.booket.core.common.constants.WebViewConstants import com.ninecraft.booket.core.data.api.repository.AuthRepository -import com.ninecraft.booket.feature.screens.BottomNavigationScreen +import com.ninecraft.booket.feature.screens.HomeScreen import com.ninecraft.booket.feature.screens.TermsAgreementScreen import com.ninecraft.booket.feature.screens.WebViewScreen import com.orhanobut.logger.Logger @@ -70,7 +70,7 @@ class TermsAgreementPresenter @AssistedInject constructor( scope.launch { authRepository.agreeTerms(true) .onSuccess { - navigator.resetRoot(BottomNavigationScreen) + navigator.resetRoot(HomeScreen) }.onFailure { exception -> exception.message?.let { Logger.e(it) } sideEffect = exception.message?.let { diff --git a/feature/login/src/main/kotlin/com/ninecraft/booket/feature/termsagreement/TermsAgreementUi.kt b/feature/login/src/main/kotlin/com/ninecraft/booket/feature/termsagreement/TermsAgreementUi.kt index 01b57d26..b130012e 100644 --- a/feature/login/src/main/kotlin/com/ninecraft/booket/feature/termsagreement/TermsAgreementUi.kt +++ b/feature/login/src/main/kotlin/com/ninecraft/booket/feature/termsagreement/TermsAgreementUi.kt @@ -1,6 +1,5 @@ package com.ninecraft.booket.feature.termsagreement -import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -9,7 +8,6 @@ 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.systemBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text @@ -27,6 +25,7 @@ import com.ninecraft.booket.core.designsystem.component.button.largeButtonStyle import com.ninecraft.booket.core.designsystem.component.checkbox.SquareCheckBox import com.ninecraft.booket.core.designsystem.theme.ReedTheme import com.ninecraft.booket.core.designsystem.theme.White +import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.feature.login.R import com.ninecraft.booket.feature.screens.TermsAgreementScreen import com.ninecraft.booket.feature.termsagreement.component.TermItem @@ -44,102 +43,106 @@ internal fun TermsAgreementUi( val termsTitles = stringArrayResource(id = R.array.terms_agreement_items) - Column( - modifier = modifier - .fillMaxSize() - .background(White) - .systemBarsPadding(), - ) { - Spacer(modifier = Modifier.height(76.dp)) + ReedScaffold( + modifier = modifier.fillMaxSize(), + containerColor = White, + ) { innerPadding -> Column( modifier = Modifier - .weight(1f) - .padding(horizontal = ReedTheme.spacing.spacing5), + .fillMaxSize() + .padding(innerPadding), ) { - Text( - text = stringResource(R.string.terms_agreement_title), - color = ReedTheme.colors.contentPrimary, - style = ReedTheme.typography.title2SemiBold, - ) - Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing4)) - Row( + Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing3)) + Column( modifier = Modifier - .fillMaxWidth() - .border( - width = 1.dp, - color = ReedTheme.colors.contentBrand, - shape = RoundedCornerShape(ReedTheme.radius.sm), - ) - .noRippleClickable { - state.eventSink(TermsAgreementUiEvent.OnAllTermsAgreedClick) - } - .padding( - horizontal = ReedTheme.spacing.spacing4, - vertical = ReedTheme.spacing.spacing5, - ), - verticalAlignment = Alignment.CenterVertically, + .weight(1f) + .padding(horizontal = ReedTheme.spacing.spacing5), ) { - SquareCheckBox( - checked = state.isAllAgreed, - onCheckedChange = { - state.eventSink(TermsAgreementUiEvent.OnAllTermsAgreedClick) - }, - ) - Spacer(modifier = Modifier.width(ReedTheme.spacing.spacing4)) Text( - text = stringResource(R.string.terms_agreement_all), + text = stringResource(R.string.terms_agreement_title), color = ReedTheme.colors.contentPrimary, - style = ReedTheme.typography.headline1SemiBold, + style = ReedTheme.typography.title2SemiBold, + ) + Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing4)) + Row( + modifier = Modifier + .fillMaxWidth() + .border( + width = 1.dp, + color = ReedTheme.colors.contentBrand, + shape = RoundedCornerShape(ReedTheme.radius.sm), + ) + .noRippleClickable { + state.eventSink(TermsAgreementUiEvent.OnAllTermsAgreedClick) + } + .padding( + horizontal = ReedTheme.spacing.spacing4, + vertical = ReedTheme.spacing.spacing5, + ), + verticalAlignment = Alignment.CenterVertically, + ) { + SquareCheckBox( + checked = state.isAllAgreed, + onCheckedChange = { + state.eventSink(TermsAgreementUiEvent.OnAllTermsAgreedClick) + }, + ) + Spacer(modifier = Modifier.width(ReedTheme.spacing.spacing4)) + Text( + text = stringResource(R.string.terms_agreement_all), + color = ReedTheme.colors.contentPrimary, + style = ReedTheme.typography.headline1SemiBold, + ) + } + Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing5)) + TermItem( + title = termsTitles[0], + checked = state.agreedTerms[0], + onCheckClick = { + state.eventSink(TermsAgreementUiEvent.OnTermItemClick(0)) + }, + onDetailClick = { + state.eventSink(TermsAgreementUiEvent.OnTermClick) + }, + ) + Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing4)) + TermItem( + title = termsTitles[1], + checked = state.agreedTerms[1], + onCheckClick = { + state.eventSink(TermsAgreementUiEvent.OnTermItemClick(1)) + }, + onDetailClick = { + state.eventSink(TermsAgreementUiEvent.OnPolicyClick) + }, + ) + Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing4)) + TermItem( + title = termsTitles[2], + checked = state.agreedTerms[2], + hasDetailAction = false, + onCheckClick = { + state.eventSink(TermsAgreementUiEvent.OnTermItemClick(2)) + }, ) } - Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing5)) - TermItem( - title = termsTitles[0], - checked = state.agreedTerms[0], - onCheckClick = { - state.eventSink(TermsAgreementUiEvent.OnTermItemClick(0)) - }, - onDetailClick = { - state.eventSink(TermsAgreementUiEvent.OnTermClick) - }, - ) - Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing4)) - TermItem( - title = termsTitles[1], - checked = state.agreedTerms[1], - onCheckClick = { - state.eventSink(TermsAgreementUiEvent.OnTermItemClick(1)) - }, - onDetailClick = { - state.eventSink(TermsAgreementUiEvent.OnPolicyClick) - }, - ) - Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing4)) - TermItem( - title = termsTitles[2], - checked = state.agreedTerms[2], - hasDetailAction = false, - onCheckClick = { - state.eventSink(TermsAgreementUiEvent.OnTermItemClick(2)) + ReedButton( + onClick = { + state.eventSink(TermsAgreementUiEvent.OnStartButtonClick) }, + sizeStyle = largeButtonStyle, + colorStyle = ReedButtonColorStyle.PRIMARY, + modifier = Modifier + .fillMaxWidth() + .padding( + start = ReedTheme.spacing.spacing5, + end = ReedTheme.spacing.spacing5, + bottom = ReedTheme.spacing.spacing4, + ), + enabled = state.isAllAgreed, + text = stringResource(R.string.terms_agreement_button_start), ) } - ReedButton( - onClick = { - state.eventSink(TermsAgreementUiEvent.OnStartButtonClick) - }, - sizeStyle = largeButtonStyle, - colorStyle = ReedButtonColorStyle.PRIMARY, - modifier = Modifier - .fillMaxWidth() - .padding( - start = ReedTheme.spacing.spacing5, - end = ReedTheme.spacing.spacing5, - bottom = ReedTheme.spacing.spacing4, - ), - enabled = state.isAllAgreed, - text = stringResource(R.string.terms_agreement_button_start), - ) } } diff --git a/feature/main/src/main/AndroidManifest.xml b/feature/main/src/main/AndroidManifest.xml index 08c79a66..497eb3ca 100644 --- a/feature/main/src/main/AndroidManifest.xml +++ b/feature/main/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@ android:exported="true" android:screenOrientation="portrait" android:windowSoftInputMode="adjustResize" - android:theme="@style/Theme.Booket"> + android:theme="@style/Theme.Booket.Splash"> diff --git a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/MainActivity.kt b/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/MainActivity.kt index be75843e..f20f85e9 100644 --- a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/MainActivity.kt +++ b/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/MainActivity.kt @@ -4,13 +4,12 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.DisposableEffect import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import com.ninecraft.booket.core.designsystem.theme.ReedTheme -import com.ninecraft.booket.core.designsystem.theme.White import com.ninecraft.booket.feature.screens.SplashScreen import com.slack.circuit.backstack.rememberSaveableBackStack import com.slack.circuit.foundation.Circuit @@ -27,18 +26,17 @@ class MainActivity : ComponentActivity() { lateinit var circuit: Circuit override fun onCreate(savedInstanceState: Bundle?) { - enableEdgeToEdge() installSplashScreen() + enableEdgeToEdge() super.onCreate(savedInstanceState) setContent { val systemUiController = rememberSystemUiController() - val isDarkTheme = isSystemInDarkTheme() DisposableEffect(systemUiController) { systemUiController.setSystemBarsColor( - color = White, - darkIcons = !isDarkTheme, + color = Color.Transparent, + darkIcons = true, isNavigationBarContrastEnforced = false, ) diff --git a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/bottomnavigation/BottomNavigationPresenter.kt b/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/bottomnavigation/BottomNavigationPresenter.kt deleted file mode 100644 index bf7b2aeb..00000000 --- a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/bottomnavigation/BottomNavigationPresenter.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.ninecraft.booket.feature.main.bottomnavigation - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import com.ninecraft.booket.feature.main.component.getCurrentTab -import com.ninecraft.booket.feature.screens.BottomNavigationScreen -import com.ninecraft.booket.feature.screens.HomeScreen -import com.slack.circuit.backstack.rememberSaveableBackStack -import com.slack.circuit.codegen.annotations.CircuitInject -import com.slack.circuit.foundation.rememberCircuitNavigator -import com.slack.circuit.runtime.Navigator -import com.slack.circuit.runtime.presenter.Presenter -import dagger.assisted.Assisted -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject -import dagger.hilt.android.components.ActivityRetainedComponent - -class BottomNavigationPresenter @AssistedInject constructor( - @Assisted private val rootNavigator: Navigator, -) : Presenter { - - @Composable - override fun present(): BottomNavigationUiState { - val childBackStack = rememberSaveableBackStack(root = HomeScreen) - val childNavigator = rememberCircuitNavigator(childBackStack) - val delegateNavigator = remember(childNavigator, rootNavigator) { - DelegateNavigator(childNavigator, rootNavigator) - } - val currentTab = getCurrentTab(childBackStack) - - fun handleEvent(event: BottomNavigationUiEvent) { - when (event) { - is BottomNavigationUiEvent.OnTabSelected -> { - childNavigator.resetRoot( - newRoot = event.tab.screen, - saveState = true, - restoreState = true, - ) - } - } - } - - return BottomNavigationUiState( - backStack = childBackStack, - navigator = delegateNavigator, - currentTab = currentTab, - eventSink = ::handleEvent, - ) - } - - @CircuitInject(BottomNavigationScreen::class, ActivityRetainedComponent::class) - @AssistedFactory - fun interface Factory { - fun create(rootNavigator: Navigator): BottomNavigationPresenter - } -} diff --git a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/bottomnavigation/BottomNavigationUi.kt b/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/bottomnavigation/BottomNavigationUi.kt deleted file mode 100644 index e16017dd..00000000 --- a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/bottomnavigation/BottomNavigationUi.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.ninecraft.booket.feature.main.bottomnavigation - -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.ninecraft.booket.core.ui.ReedScaffold -import com.ninecraft.booket.feature.main.component.MainBottomBar -import com.ninecraft.booket.feature.main.component.MainTab -import com.ninecraft.booket.feature.screens.BottomNavigationScreen -import com.slack.circuit.codegen.annotations.CircuitInject -import com.slack.circuit.foundation.NavigableCircuitContent -import dagger.hilt.android.components.ActivityRetainedComponent -import kotlinx.collections.immutable.toImmutableList - -@CircuitInject(BottomNavigationScreen::class, ActivityRetainedComponent::class) -@Composable -fun BottomNavigationUi( - state: BottomNavigationUiState, - modifier: Modifier = Modifier, -) { - ReedScaffold( - modifier = modifier.fillMaxSize(), - bottomBar = { - MainBottomBar( - tabs = MainTab.entries.toImmutableList(), - currentTab = state.currentTab, - onTabSelected = { tab -> - state.eventSink(BottomNavigationUiEvent.OnTabSelected(tab)) - }, - ) - }, - contentWindowInsets = WindowInsets(top = 0.dp), - ) { innerPadding -> - NavigableCircuitContent( - navigator = state.navigator, - backStack = state.backStack, - modifier = Modifier - .fillMaxSize() - .padding(innerPadding), - ) - } -} diff --git a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/bottomnavigation/BottomNavigationUiState.kt b/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/bottomnavigation/BottomNavigationUiState.kt deleted file mode 100644 index a01fc304..00000000 --- a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/bottomnavigation/BottomNavigationUiState.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.ninecraft.booket.feature.main.bottomnavigation - -import com.ninecraft.booket.feature.main.component.MainTab -import com.slack.circuit.backstack.SaveableBackStack -import com.slack.circuit.runtime.CircuitUiState -import com.slack.circuit.runtime.Navigator - -data class BottomNavigationUiState( - val backStack: SaveableBackStack, - val navigator: Navigator, - val currentTab: MainTab?, - val eventSink: (BottomNavigationUiEvent) -> Unit, -) : CircuitUiState - -sealed interface BottomNavigationUiEvent { - data class OnTabSelected(val tab: MainTab) : BottomNavigationUiEvent -} diff --git a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/bottomnavigation/DelegateNavigator.kt b/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/bottomnavigation/DelegateNavigator.kt deleted file mode 100644 index e3b9f581..00000000 --- a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/bottomnavigation/DelegateNavigator.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.ninecraft.booket.feature.main.bottomnavigation - -import com.ninecraft.booket.feature.main.component.MainTab -import com.slack.circuit.runtime.Navigator -import com.slack.circuit.runtime.screen.PopResult -import com.slack.circuit.runtime.screen.Screen -import kotlinx.collections.immutable.ImmutableList - -class DelegateNavigator( - private val childNavigator: Navigator, - private val rootNavigator: Navigator, -) : Navigator { - private fun isMainTabScreen(screen: Screen): Boolean { - return MainTab.entries.any { it.screen::class == screen::class } - } - - override fun goTo(screen: Screen): Boolean { - return if (isMainTabScreen(screen)) { - childNavigator.goTo(screen) - } else { - rootNavigator.goTo(screen) - } - } - - override fun pop(result: PopResult?): Screen? { - return childNavigator.pop(result) - } - - override fun peek(): Screen? { - return childNavigator.peek() - } - - override fun resetRoot( - newRoot: Screen, - saveState: Boolean, - restoreState: Boolean, - ): ImmutableList { - return if (isMainTabScreen(newRoot)) { - childNavigator.resetRoot(newRoot, saveState, restoreState) - } else { - rootNavigator.resetRoot(newRoot, saveState, restoreState) - } - } - - override fun peekBackStack(): ImmutableList { - return childNavigator.peekBackStack() - } -} diff --git a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/splash/SplashPresenter.kt b/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/splash/SplashPresenter.kt index 1e1c200c..1db7ec38 100644 --- a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/splash/SplashPresenter.kt +++ b/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/splash/SplashPresenter.kt @@ -1,24 +1,29 @@ package com.ninecraft.booket.feature.main.splash import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import com.ninecraft.booket.core.data.api.repository.AuthRepository import com.ninecraft.booket.core.data.api.repository.UserRepository import com.ninecraft.booket.core.model.AutoLoginState import com.ninecraft.booket.core.model.OnboardingState -import com.ninecraft.booket.feature.screens.BottomNavigationScreen +import com.ninecraft.booket.feature.screens.HomeScreen import com.ninecraft.booket.feature.screens.LoginScreen import com.ninecraft.booket.feature.screens.OnboardingScreen import com.ninecraft.booket.feature.screens.SplashScreen import com.skydoves.compose.effects.RememberedEffect import com.slack.circuit.codegen.annotations.CircuitInject import com.slack.circuit.retained.collectAsRetainedState +import com.slack.circuit.retained.rememberRetained import com.slack.circuit.runtime.Navigator import com.slack.circuit.runtime.presenter.Presenter import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.components.ActivityRetainedComponent +import kotlinx.coroutines.delay class SplashPresenter @AssistedInject constructor( @Assisted private val navigator: Navigator, @@ -30,8 +35,16 @@ class SplashPresenter @AssistedInject constructor( override fun present(): SplashUiState { val onboardingState by userRepository.onboardingState.collectAsRetainedState(initial = OnboardingState.IDLE) val autoLoginState by authRepository.autoLoginState.collectAsRetainedState(initial = AutoLoginState.IDLE) + var isSplashTimeCompleted by rememberRetained { mutableStateOf(false) } + + LaunchedEffect(Unit) { + delay(1000L) + isSplashTimeCompleted = true + } + + RememberedEffect(onboardingState, autoLoginState, isSplashTimeCompleted) { + if (!isSplashTimeCompleted) return@RememberedEffect - RememberedEffect(onboardingState, autoLoginState) { when (onboardingState) { OnboardingState.NOT_COMPLETED -> { navigator.resetRoot(OnboardingScreen) @@ -40,7 +53,7 @@ class SplashPresenter @AssistedInject constructor( OnboardingState.COMPLETED -> { when (autoLoginState) { AutoLoginState.LOGGED_IN -> { - navigator.resetRoot(BottomNavigationScreen) + navigator.resetRoot(HomeScreen) } AutoLoginState.NOT_LOGGED_IN -> { diff --git a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/splash/SplashUi.kt b/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/splash/SplashUi.kt index fd485ee0..d6cf9dbc 100644 --- a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/splash/SplashUi.kt +++ b/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/splash/SplashUi.kt @@ -1,17 +1,84 @@ package com.ninecraft.booket.feature.main.splash +import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.ninecraft.booket.core.designsystem.DevicePreview +import com.ninecraft.booket.core.designsystem.theme.ReedTheme +import com.ninecraft.booket.feature.main.R import com.ninecraft.booket.feature.screens.SplashScreen import com.slack.circuit.codegen.annotations.CircuitInject import dagger.hilt.android.components.ActivityRetainedComponent +import tech.thdev.compose.exteions.system.ui.controller.rememberSystemUiController @CircuitInject(SplashScreen::class, ActivityRetainedComponent::class) @Composable fun SplashUi( modifier: Modifier = Modifier, ) { - Box(modifier = modifier.fillMaxSize()) + val systemUiController = rememberSystemUiController() + + DisposableEffect(systemUiController) { + systemUiController.setSystemBarsColor( + color = Color.Transparent, + darkIcons = false, + isNavigationBarContrastEnforced = false, + ) + + onDispose { + systemUiController.setSystemBarsColor( + color = Color.Transparent, + darkIcons = true, + isNavigationBarContrastEnforced = false, + ) + } + } + + Box( + modifier = modifier + .fillMaxSize() + .background(ReedTheme.colors.bgPrimary), + contentAlignment = Alignment.Center, + ) { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Image( + painter = painterResource(R.drawable.ic_reed_logo), + contentDescription = "Reed Logo", + modifier = Modifier.width(182.dp), + ) + Spacer(Modifier.height(ReedTheme.spacing.spacing5)) + Text( + text = stringResource(R.string.splash_title), + color = ReedTheme.colors.contentInverse, + style = ReedTheme.typography.heading2SemiBold, + ) + Spacer(Modifier.height(ReedTheme.spacing.spacing8)) + } + } +} + +@DevicePreview +@Composable +private fun SplashPreview() { + ReedTheme { + SplashUi() + } } diff --git a/feature/main/src/main/res/drawable/ic_reed_logo.png b/feature/main/src/main/res/drawable/ic_reed_logo.png new file mode 100644 index 00000000..1cf1d821 Binary files /dev/null and b/feature/main/src/main/res/drawable/ic_reed_logo.png differ diff --git a/feature/main/src/main/res/values/strings.xml b/feature/main/src/main/res/values/strings.xml index e3d059c3..2ae49c00 100644 --- a/feature/main/src/main/res/values/strings.xml +++ b/feature/main/src/main/res/values/strings.xml @@ -2,4 +2,5 @@ 도서 검색 내 서재 + 책 덮기 전 한 문장을 기록해보세요 diff --git a/feature/onboarding/src/main/kotlin/com/ninecraft/booket/feature/onboarding/OnboardingUi.kt b/feature/onboarding/src/main/kotlin/com/ninecraft/booket/feature/onboarding/OnboardingUi.kt index 9be55bb5..7e95996e 100644 --- a/feature/onboarding/src/main/kotlin/com/ninecraft/booket/feature/onboarding/OnboardingUi.kt +++ b/feature/onboarding/src/main/kotlin/com/ninecraft/booket/feature/onboarding/OnboardingUi.kt @@ -18,7 +18,8 @@ import com.ninecraft.booket.core.designsystem.component.button.ReedButton import com.ninecraft.booket.core.designsystem.component.button.ReedButtonColorStyle import com.ninecraft.booket.core.designsystem.component.button.largeButtonStyle import com.ninecraft.booket.core.designsystem.theme.ReedTheme -import com.ninecraft.booket.core.ui.component.ReedFullScreen +import com.ninecraft.booket.core.designsystem.theme.White +import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.feature.onboarding.component.OnboardingPage import com.ninecraft.booket.feature.onboarding.component.PagerIndicator import com.ninecraft.booket.feature.screens.OnboardingScreen @@ -31,9 +32,14 @@ internal fun OnboardingUi( state: OnboardingUiState, modifier: Modifier = Modifier, ) { - ReedFullScreen(modifier = modifier) { + ReedScaffold( + modifier = modifier.fillMaxSize(), + containerColor = White, + ) { innerPadding -> Column( - modifier = Modifier.fillMaxSize(), + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), horizontalAlignment = Alignment.CenterHorizontally, ) { HorizontalPager( diff --git a/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrUi.kt b/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrUi.kt index 4ca473a1..4dcf88de 100644 --- a/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrUi.kt +++ b/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrUi.kt @@ -12,28 +12,24 @@ import androidx.camera.core.ImageAnalysis import androidx.camera.view.LifecycleCameraController import androidx.camera.view.PreviewView import androidx.compose.foundation.background -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement 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.WindowInsets 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.systemBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -58,6 +54,7 @@ import com.ninecraft.booket.core.designsystem.component.button.largeButtonStyle import com.ninecraft.booket.core.designsystem.theme.Neutral950 import com.ninecraft.booket.core.designsystem.theme.ReedTheme import com.ninecraft.booket.core.designsystem.theme.White +import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.core.ui.component.ReedCloseTopAppBar import com.ninecraft.booket.core.ui.component.ReedDialog import com.ninecraft.booket.feature.record.R @@ -71,21 +68,13 @@ import com.ninecraft.booket.core.designsystem.R as designR @CircuitInject(OcrScreen::class, ActivityRetainedComponent::class) @Composable -internal fun Ocr( +internal fun OcrUi( state: OcrUiState, modifier: Modifier = Modifier, ) { - Scaffold(contentWindowInsets = WindowInsets(0.dp)) { innerPadding -> - Box( - modifier = modifier - .fillMaxSize() - .padding(innerPadding), - ) { - when (state.currentUi) { - OcrUi.CAMERA -> CameraPreview(state = state, modifier = modifier) - OcrUi.RESULT -> TextScanResult(state = state, modifier = modifier) - } - } + when (state.currentUi) { + OcrUi.CAMERA -> CameraPreview(state = state, modifier = modifier) + OcrUi.RESULT -> TextScanResult(state = state, modifier = modifier) } } @@ -126,17 +115,18 @@ private fun CameraPreview( } val systemUiController = rememberSystemUiController() - val isDarkTheme = isSystemInDarkTheme() DisposableEffect(systemUiController) { systemUiController.setSystemBarsColor( - color = Neutral950, + color = Color.Transparent, + darkIcons = false, isNavigationBarContrastEnforced = false, ) + onDispose { systemUiController.setSystemBarsColor( - color = White, - darkIcons = !isDarkTheme, + color = Color.Transparent, + darkIcons = true, isNavigationBarContrastEnforced = false, ) } @@ -163,91 +153,94 @@ private fun CameraPreview( } } - Box( - modifier = modifier - .fillMaxSize() - .background(Neutral950) - .systemBarsPadding(), - ) { - Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, + ReedScaffold( + modifier = modifier.fillMaxSize(), + containerColor = Neutral950, + ) { innerPadding -> + Box( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), ) { - ReedCloseTopAppBar( - modifier = Modifier.background(color = Color.Black), - isDark = true, - onClose = { - state.eventSink(OcrUiEvent.OnCloseClick) - }, - ) - Text( - text = stringResource(R.string.ocr_guide), - color = ReedTheme.colors.contentInverse, - textAlign = TextAlign.Center, - style = ReedTheme.typography.headline2Medium, - ) - } - - if (state.hasPermission) { - Box( - modifier = Modifier - .fillMaxWidth() - .background(White) - .aspectRatio(1f) - .align(Alignment.Center), + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, ) { - AndroidView( - modifier = Modifier.fillMaxSize(), - factory = { context -> - PreviewView(context).apply { - layoutParams = LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT, - ) - clipToOutline = true - implementationMode = PreviewView.ImplementationMode.COMPATIBLE - scaleType = PreviewView.ScaleType.FILL_CENTER - controller = cameraController - } + ReedCloseTopAppBar( + isDark = true, + onClose = { + state.eventSink(OcrUiEvent.OnCloseClick) }, ) - } - CameraFrame(modifier = Modifier.align(Alignment.Center)) - } - - Column( - modifier = Modifier.align(Alignment.BottomCenter), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - if (state.isTextDetectionFailed) { Text( - text = stringResource(R.string.ocr_error_text_detection_failed), - color = ReedTheme.colors.contentError, + text = stringResource(R.string.ocr_guide), + color = ReedTheme.colors.contentInverse, textAlign = TextAlign.Center, - style = ReedTheme.typography.label2Regular, + style = ReedTheme.typography.headline2Medium, ) - Spacer(modifier = Modifier.height(22.dp)) } - Button( - onClick = { - state.eventSink(OcrUiEvent.OnCaptureButtonClick) - }, - modifier = Modifier.size(72.dp), - shape = CircleShape, - colors = ButtonDefaults.buttonColors( - containerColor = ReedTheme.colors.bgPrimary, - contentColor = White, - ), - contentPadding = PaddingValues(ReedTheme.spacing.spacing0), + if (state.hasPermission) { + Box( + modifier = Modifier + .fillMaxWidth() + .background(White) + .aspectRatio(1f) + .align(Alignment.Center), + ) { + AndroidView( + modifier = Modifier.fillMaxSize(), + factory = { context -> + PreviewView(context).apply { + layoutParams = LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + ) + clipToOutline = true + implementationMode = PreviewView.ImplementationMode.COMPATIBLE + scaleType = PreviewView.ScaleType.FILL_CENTER + controller = cameraController + } + }, + ) + } + CameraFrame(modifier = Modifier.align(Alignment.Center)) + } + + Column( + modifier = Modifier.align(Alignment.BottomCenter), + horizontalAlignment = Alignment.CenterHorizontally, ) { - Icon( - imageVector = ImageVector.vectorResource(designR.drawable.ic_maximize), - contentDescription = "Scan Icon", - modifier = Modifier.size(ReedTheme.spacing.spacing8), - ) + if (state.isTextDetectionFailed) { + Text( + text = stringResource(R.string.ocr_error_text_detection_failed), + color = ReedTheme.colors.contentError, + textAlign = TextAlign.Center, + style = ReedTheme.typography.label2Regular, + ) + Spacer(modifier = Modifier.height(22.dp)) + } + + Button( + onClick = { + state.eventSink(OcrUiEvent.OnCaptureButtonClick) + }, + modifier = Modifier.size(72.dp), + shape = CircleShape, + colors = ButtonDefaults.buttonColors( + containerColor = ReedTheme.colors.bgPrimary, + contentColor = White, + ), + contentPadding = PaddingValues(ReedTheme.spacing.spacing0), + ) { + Icon( + imageVector = ImageVector.vectorResource(designR.drawable.ic_maximize), + contentDescription = "Scan Icon", + modifier = Modifier.size(ReedTheme.spacing.spacing8), + ) + } + Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing4)) } - Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing4)) } } @@ -271,63 +264,67 @@ private fun TextScanResult( state: OcrUiState, modifier: Modifier = Modifier, ) { - Column( - modifier = modifier - .fillMaxSize() - .background(White) - .systemBarsPadding(), - ) { - ReedCloseTopAppBar( - title = stringResource(R.string.ocr_sentence_selection), - onClose = { - state.eventSink(OcrUiEvent.OnCloseClick) - }, - ) - LazyColumn( + ReedScaffold( + modifier = modifier.fillMaxSize(), + containerColor = White, + ) { innerPadding -> + Column( modifier = Modifier - .weight(1f) - .padding(horizontal = ReedTheme.spacing.spacing3), - verticalArrangement = Arrangement.spacedBy(ReedTheme.spacing.spacing2), + .fillMaxSize() + .padding(innerPadding), ) { - items(state.sentenceList.size) { index -> - SentenceBox( + ReedCloseTopAppBar( + title = stringResource(R.string.ocr_sentence_selection), + onClose = { + state.eventSink(OcrUiEvent.OnCloseClick) + }, + ) + LazyColumn( + modifier = Modifier + .weight(1f) + .padding(horizontal = ReedTheme.spacing.spacing3), + verticalArrangement = Arrangement.spacedBy(ReedTheme.spacing.spacing2), + ) { + items(state.sentenceList.size) { index -> + SentenceBox( + onClick = { + state.eventSink(OcrUiEvent.OnSentenceSelected(index)) + }, + sentence = state.sentenceList[index], + isSelected = state.selectedIndices.contains(index), + ) + } + } + Row( + modifier = Modifier + .fillMaxWidth() + .padding( + horizontal = ReedTheme.spacing.spacing5, + vertical = ReedTheme.spacing.spacing4, + ), + ) { + ReedButton( + onClick = { + state.eventSink(OcrUiEvent.OnReCaptureButtonClick) + }, + sizeStyle = largeButtonStyle, + colorStyle = ReedButtonColorStyle.SECONDARY, + modifier = Modifier.weight(1f), + text = stringResource(R.string.ocr_recapture), + ) + Spacer(modifier = Modifier.width(ReedTheme.spacing.spacing2)) + ReedButton( onClick = { - state.eventSink(OcrUiEvent.OnSentenceSelected(index)) + state.eventSink(OcrUiEvent.OnSelectionConfirmed) }, - sentence = state.sentenceList[index], - isSelected = state.selectedIndices.contains(index), + sizeStyle = largeButtonStyle, + colorStyle = ReedButtonColorStyle.PRIMARY, + enabled = state.selectedIndices.isNotEmpty(), + modifier = Modifier.weight(1f), + text = stringResource(R.string.ocr_selection_confirm), ) } } - Row( - modifier = Modifier - .fillMaxWidth() - .padding( - horizontal = ReedTheme.spacing.spacing5, - vertical = ReedTheme.spacing.spacing4, - ), - ) { - ReedButton( - onClick = { - state.eventSink(OcrUiEvent.OnReCaptureButtonClick) - }, - sizeStyle = largeButtonStyle, - colorStyle = ReedButtonColorStyle.SECONDARY, - modifier = Modifier.weight(1f), - text = stringResource(R.string.ocr_recapture), - ) - Spacer(modifier = Modifier.width(ReedTheme.spacing.spacing2)) - ReedButton( - onClick = { - state.eventSink(OcrUiEvent.OnSelectionConfirmed) - }, - sizeStyle = largeButtonStyle, - colorStyle = ReedButtonColorStyle.PRIMARY, - enabled = state.selectedIndices.isNotEmpty(), - modifier = Modifier.weight(1f), - text = stringResource(R.string.ocr_selection_confirm), - ) - } } if (state.isRecaptureDialogVisible) { diff --git a/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterPresenter.kt b/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterPresenter.kt index c646d6c0..9c7dc634 100644 --- a/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterPresenter.kt +++ b/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterPresenter.kt @@ -18,7 +18,7 @@ import com.ninecraft.booket.feature.screens.LoginScreen import com.ninecraft.booket.feature.screens.OcrScreen import com.ninecraft.booket.feature.screens.RecordDetailScreen import com.ninecraft.booket.feature.screens.RecordScreen -import com.ninecraft.booket.feature.screens.delayedPop +import com.ninecraft.booket.feature.screens.extensions.delayedPop import com.orhanobut.logger.Logger import com.slack.circuit.codegen.annotations.CircuitInject import com.slack.circuit.foundation.rememberAnsweringNavigator diff --git a/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUi.kt b/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUi.kt index 5ca9b1d8..6d3ce3f4 100644 --- a/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUi.kt +++ b/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUi.kt @@ -3,6 +3,7 @@ package com.ninecraft.booket.feature.record.register import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -20,9 +21,10 @@ import com.ninecraft.booket.core.designsystem.component.button.ReedButton import com.ninecraft.booket.core.designsystem.component.button.ReedButtonColorStyle import com.ninecraft.booket.core.designsystem.component.button.largeButtonStyle import com.ninecraft.booket.core.designsystem.theme.ReedTheme +import com.ninecraft.booket.core.designsystem.theme.White +import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.core.ui.component.ReedBackTopAppBar import com.ninecraft.booket.core.ui.component.ReedDialog -import com.ninecraft.booket.core.ui.component.ReedFullScreen import com.ninecraft.booket.feature.record.R import com.ninecraft.booket.feature.record.step.EmotionStep import com.ninecraft.booket.feature.record.step.ImpressionStep @@ -43,47 +45,54 @@ internal fun RecordRegister( state.eventSink(RecordRegisterUiEvent.OnBackButtonClick) } - ReedFullScreen( + ReedScaffold( modifier = modifier.fillMaxSize(), - ) { - ReedBackTopAppBar( - onBackClick = { - state.eventSink(RecordRegisterUiEvent.OnBackButtonClick) - }, - ) - RecordProgressBar( - currentStep = state.currentStep, - modifier = modifier.padding(horizontal = ReedTheme.spacing.spacing5), - ) - Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing10)) - when (state.currentStep) { - RecordStep.QUOTE -> { - QuoteStep(state = state) - } + containerColor = White, + ) { innerPadding -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), + ) { + ReedBackTopAppBar( + onBackClick = { + state.eventSink(RecordRegisterUiEvent.OnBackButtonClick) + }, + ) + RecordProgressBar( + currentStep = state.currentStep, + modifier = modifier.padding(horizontal = ReedTheme.spacing.spacing5), + ) + Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing10)) + when (state.currentStep) { + RecordStep.QUOTE -> { + QuoteStep(state = state) + } - RecordStep.EMOTION -> { - EmotionStep(state = state) - } + RecordStep.EMOTION -> { + EmotionStep(state = state) + } - RecordStep.IMPRESSION -> { - ImpressionStep(state = state) + RecordStep.IMPRESSION -> { + ImpressionStep(state = state) + } } + Spacer(modifier = Modifier.weight(1f)) + ReedButton( + onClick = { + state.eventSink(RecordRegisterUiEvent.OnNextButtonClick) + }, + colorStyle = ReedButtonColorStyle.PRIMARY, + sizeStyle = largeButtonStyle, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = ReedTheme.spacing.spacing5), + enabled = state.isNextButtonEnabled, + text = stringResource(R.string.record_next_button), + multipleEventsCutterEnabled = state.currentStep == RecordStep.IMPRESSION, + ) + Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing4)) } - Spacer(modifier = Modifier.weight(1f)) - ReedButton( - onClick = { - state.eventSink(RecordRegisterUiEvent.OnNextButtonClick) - }, - colorStyle = ReedButtonColorStyle.PRIMARY, - sizeStyle = largeButtonStyle, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = ReedTheme.spacing.spacing5), - enabled = state.isNextButtonEnabled, - text = stringResource(R.string.record_next_button), - multipleEventsCutterEnabled = state.currentStep == RecordStep.IMPRESSION, - ) - Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing4)) } if (state.isExitDialogVisible) { diff --git a/feature/screens/build.gradle.kts b/feature/screens/build.gradle.kts index fb9f4762..cf55a687 100644 --- a/feature/screens/build.gradle.kts +++ b/feature/screens/build.gradle.kts @@ -1,5 +1,6 @@ plugins { alias(libs.plugins.booket.android.library) + alias(libs.plugins.booket.android.library.compose) alias(libs.plugins.kotlin.parcelize) } @@ -8,6 +9,14 @@ android { } dependencies { - implementation(projects.core.model) + implementations( + projects.core.designsystem, + projects.core.model, + + libs.kotlinx.collections.immutable, + + libs.circuit.foundation, + libs.compose.shadow, + ) api(libs.circuit.runtime) } diff --git a/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/Screens.kt b/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/Screens.kt index c387543d..d25439af 100644 --- a/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/Screens.kt +++ b/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/Screens.kt @@ -8,9 +8,6 @@ abstract class ReedScreen(val name: String) : Screen { override fun toString(): String = name } -@Parcelize -data object BottomNavigationScreen : ReedScreen(name = "BottomNavigation()") - @Parcelize data object HomeScreen : ReedScreen(name = "Home()") diff --git a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/component/MainBottomBar.kt b/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/component/MainBottomBar.kt similarity index 98% rename from feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/component/MainBottomBar.kt rename to feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/component/MainBottomBar.kt index fe50f72f..a23c9bcb 100644 --- a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/component/MainBottomBar.kt +++ b/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/component/MainBottomBar.kt @@ -1,4 +1,4 @@ -package com.ninecraft.booket.feature.main.component +package com.ninecraft.booket.feature.screens.component import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -38,7 +38,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList @Composable -internal fun MainBottomBar( +fun MainBottomBar( tabs: ImmutableList, currentTab: MainTab?, onTabSelected: (MainTab) -> Unit, diff --git a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/component/MainTab.kt b/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/component/MainTab.kt similarity index 90% rename from feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/component/MainTab.kt rename to feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/component/MainTab.kt index 974d6d7a..2395c416 100644 --- a/feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/component/MainTab.kt +++ b/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/component/MainTab.kt @@ -1,10 +1,10 @@ -package com.ninecraft.booket.feature.main.component +package com.ninecraft.booket.feature.screens.component import androidx.annotation.DrawableRes import androidx.annotation.StringRes -import com.ninecraft.booket.feature.main.R import com.ninecraft.booket.feature.screens.HomeScreen import com.ninecraft.booket.feature.screens.LibraryScreen +import com.ninecraft.booket.feature.screens.R import com.slack.circuit.runtime.screen.Screen enum class MainTab( diff --git a/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/extensions.kt b/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/extensions/Navigator.kt similarity index 72% rename from feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/extensions.kt rename to feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/extensions/Navigator.kt index c0cf1e0b..521365ed 100644 --- a/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/extensions.kt +++ b/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/extensions/Navigator.kt @@ -1,5 +1,6 @@ -package com.ninecraft.booket.feature.screens +package com.ninecraft.booket.feature.screens.extensions +import com.ninecraft.booket.feature.screens.ReedScreen import com.slack.circuit.runtime.Navigator import kotlinx.coroutines.delay diff --git a/feature/screens/src/main/res/drawable/ic_home.xml b/feature/screens/src/main/res/drawable/ic_home.xml new file mode 100644 index 00000000..d375aa5d --- /dev/null +++ b/feature/screens/src/main/res/drawable/ic_home.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/screens/src/main/res/drawable/ic_library.xml b/feature/screens/src/main/res/drawable/ic_library.xml new file mode 100644 index 00000000..21bfdc57 --- /dev/null +++ b/feature/screens/src/main/res/drawable/ic_library.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/screens/src/main/res/drawable/ic_selected_home.xml b/feature/screens/src/main/res/drawable/ic_selected_home.xml new file mode 100644 index 00000000..b99fb272 --- /dev/null +++ b/feature/screens/src/main/res/drawable/ic_selected_home.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/screens/src/main/res/drawable/ic_selected_library.xml b/feature/screens/src/main/res/drawable/ic_selected_library.xml new file mode 100644 index 00000000..2f02c9ac --- /dev/null +++ b/feature/screens/src/main/res/drawable/ic_selected_library.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/screens/src/main/res/values/strings.xml b/feature/screens/src/main/res/values/strings.xml new file mode 100644 index 00000000..f2b5f6f2 --- /dev/null +++ b/feature/screens/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + 내 서재 + \ No newline at end of file diff --git a/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchPresenter.kt b/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchPresenter.kt index 14d5c2c3..4ba016d9 100644 --- a/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchPresenter.kt +++ b/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchPresenter.kt @@ -17,7 +17,7 @@ import com.ninecraft.booket.core.ui.component.FooterState import com.ninecraft.booket.feature.screens.LoginScreen import com.ninecraft.booket.feature.screens.RecordScreen import com.ninecraft.booket.feature.screens.SearchScreen -import com.ninecraft.booket.feature.screens.delayedGoTo +import com.ninecraft.booket.feature.screens.extensions.delayedGoTo import com.orhanobut.logger.Logger import com.slack.circuit.codegen.annotations.CircuitInject import com.slack.circuit.retained.collectAsRetainedState diff --git a/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchUi.kt b/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchUi.kt index 0f9f2f7f..9b48220d 100644 --- a/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchUi.kt +++ b/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchUi.kt @@ -1,7 +1,6 @@ package com.ninecraft.booket.feature.search.book import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -26,14 +25,14 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.ninecraft.booket.core.common.constants.BookStatus import com.ninecraft.booket.core.designsystem.DevicePreview -import com.ninecraft.booket.core.designsystem.component.textfield.ReedTextField -import com.ninecraft.booket.core.ui.component.ReedBackTopAppBar import com.ninecraft.booket.core.designsystem.component.ReedDivider +import com.ninecraft.booket.core.designsystem.component.textfield.ReedTextField import com.ninecraft.booket.core.designsystem.theme.ReedTheme import com.ninecraft.booket.core.designsystem.theme.White +import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.core.ui.component.InfinityLazyColumn import com.ninecraft.booket.core.ui.component.LoadStateFooter -import com.ninecraft.booket.core.ui.component.ReedFullScreen +import com.ninecraft.booket.core.ui.component.ReedBackTopAppBar import com.ninecraft.booket.feature.screens.SearchScreen import com.ninecraft.booket.feature.search.R import com.ninecraft.booket.feature.search.book.component.BookItem @@ -55,17 +54,26 @@ internal fun SearchUi( ) { HandleBookSearchSideEffects(state = state) - ReedFullScreen(modifier = modifier) { - ReedBackTopAppBar( - title = stringResource(R.string.search_title), - onBackClick = { - state.eventSink(BookSearchUiEvent.OnBackClick) - }, - ) - SearchContent( - state = state, - modifier = modifier, - ) + ReedScaffold( + modifier = modifier.fillMaxSize(), + containerColor = White, + ) { innerPadding -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), + ) { + ReedBackTopAppBar( + title = stringResource(R.string.search_title), + onBackClick = { + state.eventSink(BookSearchUiEvent.OnBackClick) + }, + ) + SearchContent( + state = state, + modifier = Modifier, + ) + } } } @@ -80,9 +88,7 @@ internal fun SearchContent( val coroutineScope = rememberCoroutineScope() Column( - modifier = modifier - .fillMaxSize() - .background(White), + modifier = modifier.fillMaxSize(), ) { Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing3)) ReedTextField( diff --git a/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/library/LibrarySearchUi.kt b/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/library/LibrarySearchUi.kt index 3ccfa9a7..d4e29d1e 100644 --- a/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/library/LibrarySearchUi.kt +++ b/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/library/LibrarySearchUi.kt @@ -1,9 +1,9 @@ package com.ninecraft.booket.feature.search.library -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -24,10 +24,10 @@ import com.ninecraft.booket.core.designsystem.component.ReedDivider import com.ninecraft.booket.core.designsystem.component.textfield.ReedTextField import com.ninecraft.booket.core.designsystem.theme.ReedTheme import com.ninecraft.booket.core.designsystem.theme.White +import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.core.ui.component.InfinityLazyColumn import com.ninecraft.booket.core.ui.component.LoadStateFooter import com.ninecraft.booket.core.ui.component.ReedBackTopAppBar -import com.ninecraft.booket.core.ui.component.ReedFullScreen import com.ninecraft.booket.feature.screens.LibrarySearchScreen import com.ninecraft.booket.feature.search.R import com.ninecraft.booket.feature.search.common.component.RecentSearchTitle @@ -44,16 +44,13 @@ internal fun LibrarySearchUi( ) { HandlingLibrarySearchSideEffect(state = state) - ReedFullScreen(modifier = modifier) { - ReedBackTopAppBar( - title = stringResource(R.string.library_search_title), - onBackClick = { - state.eventSink(LibrarySearchUiEvent.OnBackClick) - }, - ) + ReedScaffold( + modifier = modifier, + containerColor = White, + ) { innerPadding -> LibrarySearchContent( state = state, - modifier = modifier, + innerPadding = innerPadding, ) } } @@ -61,13 +58,20 @@ internal fun LibrarySearchUi( @Composable internal fun LibrarySearchContent( state: LibrarySearchUiState, + innerPadding: PaddingValues, modifier: Modifier = Modifier, ) { Column( modifier = modifier .fillMaxSize() - .background(White), + .padding(innerPadding), ) { + ReedBackTopAppBar( + title = stringResource(R.string.library_search_title), + onBackClick = { + state.eventSink(LibrarySearchUiEvent.OnBackClick) + }, + ) Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing3)) ReedTextField( queryState = state.queryState, diff --git a/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/SettingsUi.kt b/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/SettingsUi.kt index 5e0951a4..a81185c8 100644 --- a/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/SettingsUi.kt +++ b/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/SettingsUi.kt @@ -9,7 +9,6 @@ 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.systemBarsPadding import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -29,6 +28,7 @@ import com.ninecraft.booket.core.common.extensions.clickableSingle import com.ninecraft.booket.core.designsystem.DevicePreview import com.ninecraft.booket.core.ui.component.ReedDialog import com.ninecraft.booket.core.ui.component.ReedBackTopAppBar +import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.core.designsystem.component.ReedDivider import com.ninecraft.booket.core.designsystem.theme.ReedTheme import com.ninecraft.booket.core.designsystem.theme.White @@ -57,130 +57,135 @@ internal fun SettingsUi( }.getOrNull() ?: "Unknown" } - Column( + ReedScaffold( modifier = modifier .fillMaxSize() - .background(White) - .systemBarsPadding(), - ) { - ReedBackTopAppBar( - title = stringResource(R.string.settings_title), - onBackClick = { - state.eventSink(SettingsUiEvent.OnBackClick) - }, - ) - Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing4)) + .background(White), + containerColor = White, + ) { innerPadding -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), + ) { + ReedBackTopAppBar( + title = stringResource(R.string.settings_title), + onBackClick = { + state.eventSink(SettingsUiEvent.OnBackClick) + }, + ) + Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing4)) + SettingItem( + title = stringResource(R.string.settings_privacy_policy), + onItemClick = { + state.eventSink(SettingsUiEvent.OnPolicyClick) + }, + action = { + Icon( + imageVector = ImageVector.vectorResource(id = com.ninecraft.booket.core.designsystem.R.drawable.ic_chevron_right), + contentDescription = "Right Chevron Icon", + tint = Color.Unspecified, + ) + }, + ) + SettingItem( + title = stringResource(R.string.settings_terms_of_service), + onItemClick = { + state.eventSink(SettingsUiEvent.OnTermClick) + }, + action = { + Icon( + imageVector = ImageVector.vectorResource(id = com.ninecraft.booket.core.designsystem.R.drawable.ic_chevron_right), + contentDescription = "Right Chevron Icon", + tint = Color.Unspecified, + ) + }, + ) + SettingItem( + title = stringResource(R.string.settings_open_source_license), + onItemClick = { + state.eventSink(SettingsUiEvent.OnOssLicensesClick) + }, + action = { + Icon( + imageVector = ImageVector.vectorResource(id = com.ninecraft.booket.core.designsystem.R.drawable.ic_chevron_right), + contentDescription = "Right Chevron Icon", + tint = Color.Unspecified, + ) + }, + ) + SettingItem( + title = stringResource(R.string.settings_app_version), + isClickable = false, + action = { + Text( + text = appVersion, + style = ReedTheme.typography.body1Medium, + color = ReedTheme.colors.contentSecondary, + ) + }, + ) + ReedDivider(modifier = Modifier.padding(vertical = ReedTheme.spacing.spacing4)) + SettingItem( + title = stringResource(R.string.settings_logout), + onItemClick = { + state.eventSink(SettingsUiEvent.OnLogoutClick) + }, + ) + SettingItem( + title = stringResource(R.string.settings_withdraw), + onItemClick = { + state.eventSink(SettingsUiEvent.OnWithdrawClick) + }, + ) + } - SettingItem( - title = stringResource(R.string.settings_privacy_policy), - onItemClick = { - state.eventSink(SettingsUiEvent.OnPolicyClick) - }, - action = { - Icon( - imageVector = ImageVector.vectorResource(id = com.ninecraft.booket.core.designsystem.R.drawable.ic_chevron_right), - contentDescription = "Right Chevron Icon", - tint = Color.Unspecified, - ) - }, - ) - SettingItem( - title = stringResource(R.string.settings_terms_of_service), - onItemClick = { - state.eventSink(SettingsUiEvent.OnTermClick) - }, - action = { - Icon( - imageVector = ImageVector.vectorResource(id = com.ninecraft.booket.core.designsystem.R.drawable.ic_chevron_right), - contentDescription = "Right Chevron Icon", - tint = Color.Unspecified, - ) - }, - ) - SettingItem( - title = stringResource(R.string.settings_open_source_license), - onItemClick = { - state.eventSink(SettingsUiEvent.OnOssLicensesClick) - }, - action = { - Icon( - imageVector = ImageVector.vectorResource(id = com.ninecraft.booket.core.designsystem.R.drawable.ic_chevron_right), - contentDescription = "Right Chevron Icon", - tint = Color.Unspecified, + if (state.isLoading) { + Box( + modifier = Modifier.fillMaxSize(), + ) { + CircularProgressIndicator( + modifier = Modifier.align(Alignment.Center), + color = ReedTheme.colors.contentBrand, ) - }, - ) - SettingItem( - title = stringResource(R.string.settings_app_version), - isClickable = false, - action = { - Text( - text = appVersion, - style = ReedTheme.typography.body1Medium, - color = ReedTheme.colors.contentSecondary, - ) - }, - ) - ReedDivider(modifier = Modifier.padding(vertical = ReedTheme.spacing.spacing4)) - SettingItem( - title = stringResource(R.string.settings_logout), - onItemClick = { - state.eventSink(SettingsUiEvent.OnLogoutClick) - }, - ) - SettingItem( - title = stringResource(R.string.settings_withdraw), - onItemClick = { - state.eventSink(SettingsUiEvent.OnWithdrawClick) - }, - ) - } + } + } - if (state.isLoading) { - Box( - modifier = Modifier.fillMaxSize(), - ) { - CircularProgressIndicator( - modifier = Modifier.align(Alignment.Center), - color = ReedTheme.colors.contentBrand, + if (state.isLogoutDialogVisible) { + ReedDialog( + title = stringResource(R.string.settings_logout_title), + confirmButtonText = stringResource(R.string.settings_logout), + dismissButtonText = stringResource(R.string.settings_cancel), + onConfirmRequest = { + state.eventSink(SettingsUiEvent.Logout) + }, + onDismissRequest = { + state.eventSink(SettingsUiEvent.OnBottomSheetDismissed) + }, ) } - } - - if (state.isLogoutDialogVisible) { - ReedDialog( - title = stringResource(R.string.settings_logout_title), - confirmButtonText = stringResource(R.string.settings_logout), - dismissButtonText = stringResource(R.string.settings_cancel), - onConfirmRequest = { - state.eventSink(SettingsUiEvent.Logout) - }, - onDismissRequest = { - state.eventSink(SettingsUiEvent.OnBottomSheetDismissed) - }, - ) - } - if (state.isWithdrawBottomSheetVisible) { - WithdrawConfirmationBottomSheet( - onDismissRequest = { - state.eventSink(SettingsUiEvent.OnBottomSheetDismissed) - }, - sheetState = withDrawSheetState, - isCheckBoxChecked = state.isWithdrawConfirmed, - onCheckBoxCheckedChange = { - state.eventSink(SettingsUiEvent.OnWithdrawConfirmationToggled) - }, - onCancelButtonClick = { - coroutineScope.launch { - withDrawSheetState.hide() + if (state.isWithdrawBottomSheetVisible) { + WithdrawConfirmationBottomSheet( + onDismissRequest = { state.eventSink(SettingsUiEvent.OnBottomSheetDismissed) - } - }, - onWithdrawButtonClick = { - state.eventSink(SettingsUiEvent.Withdraw) - }, - ) + }, + sheetState = withDrawSheetState, + isCheckBoxChecked = state.isWithdrawConfirmed, + onCheckBoxCheckedChange = { + state.eventSink(SettingsUiEvent.OnWithdrawConfirmationToggled) + }, + onCancelButtonClick = { + coroutineScope.launch { + withDrawSheetState.hide() + state.eventSink(SettingsUiEvent.OnBottomSheetDismissed) + } + }, + onWithdrawButtonClick = { + state.eventSink(SettingsUiEvent.Withdraw) + }, + ) + } } } diff --git a/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/osslicenses/OssLicensesUi.kt b/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/osslicenses/OssLicensesUi.kt index d77621f0..3c31ff3e 100644 --- a/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/osslicenses/OssLicensesUi.kt +++ b/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/osslicenses/OssLicensesUi.kt @@ -10,7 +10,6 @@ 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.systemBarsPadding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn @@ -32,6 +31,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import com.ninecraft.booket.core.designsystem.DevicePreview import com.ninecraft.booket.core.ui.component.ReedBackTopAppBar +import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.core.designsystem.theme.ReedTheme import com.ninecraft.booket.core.designsystem.theme.White import com.ninecraft.booket.feature.settings.R @@ -59,25 +59,29 @@ internal fun OssLicenses( } } - Column( - modifier = modifier - .fillMaxSize() - .background(White) - .systemBarsPadding(), - ) { - ReedBackTopAppBar( - title = stringResource(R.string.oss_licenses_title), - onBackClick = { - state.eventSink(OssLicensesUiEvent.OnBackClicked) - }, - ) - LazyColumn { - items(licenses) { license -> - OssLicenseItem( - name = license.name, - license = license.license, - url = license.url, - ) + ReedScaffold( + modifier = modifier.fillMaxSize(), + containerColor = White, + ) { innerPadding -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), + ) { + ReedBackTopAppBar( + title = stringResource(R.string.oss_licenses_title), + onBackClick = { + state.eventSink(OssLicensesUiEvent.OnBackClicked) + }, + ) + LazyColumn { + items(licenses) { license -> + OssLicenseItem( + name = license.name, + license = license.license, + url = license.url, + ) + } } } } diff --git a/feature/webview/src/main/kotlin/com/ninecraft/booket/feature/webview/WebViewUi.kt b/feature/webview/src/main/kotlin/com/ninecraft/booket/feature/webview/WebViewUi.kt index 7eeb98c7..4835072d 100644 --- a/feature/webview/src/main/kotlin/com/ninecraft/booket/feature/webview/WebViewUi.kt +++ b/feature/webview/src/main/kotlin/com/ninecraft/booket/feature/webview/WebViewUi.kt @@ -4,15 +4,17 @@ import android.annotation.SuppressLint import android.view.ViewGroup import android.webkit.WebView import android.webkit.WebViewClient -import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView import com.ninecraft.booket.core.designsystem.DevicePreview -import com.ninecraft.booket.core.ui.component.ReedBackTopAppBar import com.ninecraft.booket.core.designsystem.theme.ReedTheme -import com.ninecraft.booket.core.ui.component.ReedFullScreen +import com.ninecraft.booket.core.ui.ReedScaffold +import com.ninecraft.booket.core.ui.component.ReedBackTopAppBar import com.ninecraft.booket.feature.screens.WebViewScreen import com.slack.circuit.codegen.annotations.CircuitInject import dagger.hilt.android.components.ActivityRetainedComponent @@ -23,16 +25,12 @@ internal fun WebViewUi( state: WebViewUiState, modifier: Modifier = Modifier, ) { - ReedFullScreen(modifier = modifier) { - ReedBackTopAppBar( - title = state.title, - onBackClick = { - state.eventSink(WebViewUiEvent.OnBackButtonClick) - }, - ) + ReedScaffold( + modifier = modifier.fillMaxSize(), + ) { innerPadding -> WebViewContent( state = state, - modifier = modifier, + innerPadding = innerPadding, ) } } @@ -41,9 +39,20 @@ internal fun WebViewUi( @Composable internal fun WebViewContent( state: WebViewUiState, + innerPadding: PaddingValues, modifier: Modifier = Modifier, ) { - Box(modifier = modifier.fillMaxSize()) { + Column( + modifier = modifier + .fillMaxSize() + .padding(innerPadding), + ) { + ReedBackTopAppBar( + title = state.title, + onBackClick = { + state.eventSink(WebViewUiEvent.OnBackButtonClick) + }, + ) AndroidView( factory = { context -> WebView(context).apply {