diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a6403f54..952f9c8e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -10,9 +10,9 @@ android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" - android:icon="@mipmap/ic_launcher" + android:icon="@drawable/img_app_icon" android:label="@string/app_name" - android:roundIcon="@mipmap/ic_launcher_round" + android:roundIcon="@drawable/img_app_icon" android:supportsRtl="true" android:theme="@style/Theme.BBANGZIP" android:usesCleartextTraffic="true" diff --git a/app/src/main/java/org/android/bbangzip/data/dto/response/ResponseExamNameDto.kt b/app/src/main/java/org/android/bbangzip/data/dto/response/ResponseExamNameDto.kt index d6972458..b809659d 100644 --- a/app/src/main/java/org/android/bbangzip/data/dto/response/ResponseExamNameDto.kt +++ b/app/src/main/java/org/android/bbangzip/data/dto/response/ResponseExamNameDto.kt @@ -12,7 +12,7 @@ data class ResponseExamNameDto( @SerialName("examDday") val examDday: Int, @SerialName("motivationMessage") - val motivationMessage: String, + val motivationMessage: String?, @SerialName("studyList") val studyList: List, ) { @@ -20,7 +20,7 @@ data class ResponseExamNameDto( SubjectDetailInfoEntity( examDate = examDate, examDday = examDday, - motivationMessage = motivationMessage, + motivationMessage = motivationMessage ?: "", todoList = studyList.map { it.toToDoCardEntity() }, ) } diff --git a/app/src/main/java/org/android/bbangzip/data/repositoryImpl/local/UserLocalRepositoryImpl.kt b/app/src/main/java/org/android/bbangzip/data/repositoryImpl/local/UserLocalRepositoryImpl.kt index 7cf2d19b..162624a9 100644 --- a/app/src/main/java/org/android/bbangzip/data/repositoryImpl/local/UserLocalRepositoryImpl.kt +++ b/app/src/main/java/org/android/bbangzip/data/repositoryImpl/local/UserLocalRepositoryImpl.kt @@ -1,6 +1,7 @@ package org.android.bbangzip.data.repositoryImpl.local import kotlinx.coroutines.flow.Flow +import org.android.bbangzip.BadgeInfo import org.android.bbangzip.OnboardingInfo import org.android.bbangzip.UserPreferences import org.android.bbangzip.data.datasource.local.UserLocalDataSource @@ -103,4 +104,39 @@ class UserLocalRepositoryImpl .build() } } + + override suspend fun setIsBadgeAvailable(isBadgeAvailable: Boolean) { + userDataSource.updateUserPreferences { userData -> + userData.toBuilder() + .setIsBadgeAvailable(isBadgeAvailable) + .build() + } + } + + override suspend fun setIsOnOnboardingDone(isOnboardingDone: Boolean) { + userDataSource.updateUserPreferences { userData -> + userData.toBuilder() + .setIsOnboardingDone(isOnboardingDone) + .build() + } + } + + override suspend fun setBadgeInfo( + badgeName: String, + badgeImage: String, + hashTags: List, + ) { + userDataSource.updateUserPreferences { userData -> + val badgeInfo = + BadgeInfo.newBuilder() + .setBadgeName(badgeName) + .setBadgeImage(badgeImage) + .addAllHashTags(hashTags) + .build() + + userData.toBuilder() + .addBadges(badgeInfo) + .build() + } + } } diff --git a/app/src/main/java/org/android/bbangzip/domain/model/BadgeCardListEntity.kt b/app/src/main/java/org/android/bbangzip/domain/model/BadgeCardListEntity.kt index 9340e23d..b7cd4710 100644 --- a/app/src/main/java/org/android/bbangzip/domain/model/BadgeCardListEntity.kt +++ b/app/src/main/java/org/android/bbangzip/domain/model/BadgeCardListEntity.kt @@ -1,5 +1,7 @@ package org.android.bbangzip.domain.model +import org.android.bbangzip.presentation.model.Badge + data class BadgeCardListEntity( val badgeCardList: List, ) @@ -8,4 +10,11 @@ data class BadgeCardEntity( val badgeImage: String, val badgeName: String, val hashTags: List, -) +) { + fun toBadge() = + Badge( + badgeName = badgeName, + badgeImg = badgeImage, + hashTags = hashTags, + ) +} diff --git a/app/src/main/java/org/android/bbangzip/domain/repository/local/UserLocalRepository.kt b/app/src/main/java/org/android/bbangzip/domain/repository/local/UserLocalRepository.kt index e1e70d7a..37fcdd30 100644 --- a/app/src/main/java/org/android/bbangzip/domain/repository/local/UserLocalRepository.kt +++ b/app/src/main/java/org/android/bbangzip/domain/repository/local/UserLocalRepository.kt @@ -26,4 +26,14 @@ interface UserLocalRepository { suspend fun setIsOnboardingCompleted(isOnboardingCompleted: Boolean) suspend fun clearOnboardingInfo() + + suspend fun setIsOnOnboardingDone(isOnboardingDone: Boolean) + + suspend fun setIsBadgeAvailable(isBadgeAvailable: Boolean) + + suspend fun setBadgeInfo( + badgeName: String, + badgeImage: String, + hashTags: List, + ) } diff --git a/app/src/main/java/org/android/bbangzip/presentation/component/bottomsheet/BbangZipGetBadgeBottomSheet.kt b/app/src/main/java/org/android/bbangzip/presentation/component/bottomsheet/BbangZipGetBadgeBottomSheet.kt index 3761b8f1..41fcbc17 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/component/bottomsheet/BbangZipGetBadgeBottomSheet.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/component/bottomsheet/BbangZipGetBadgeBottomSheet.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.Text @@ -25,6 +26,7 @@ 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.draw.clip import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -220,7 +222,10 @@ private fun BadgeDetailContent( .crossfade(enable = true) .build(), contentDescription = null, - modifier = Modifier.align(Alignment.CenterHorizontally), + modifier = + Modifier + .clip(shape = RoundedCornerShape(48.dp)) + .align(Alignment.CenterHorizontally), ) Spacer(modifier = Modifier.height(8.dp)) diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/login/LoginContract.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/login/LoginContract.kt index f902271c..bba6681d 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/login/LoginContract.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/login/LoginContract.kt @@ -13,9 +13,8 @@ class LoginContract { val isOnboardingCompleted: Boolean = false, val onBoardingList: List = immutableListOf( - R.drawable.img_login1, - R.drawable.img_login2, - R.drawable.img_login1, + R.drawable.img_onboarding_first, + R.drawable.img_onboarding_second, ), val loginState: Boolean = false, ) : BaseContract.State, Parcelable { diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/login/LoginNavigation.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/login/LoginNavigation.kt index 4aaf0ee9..374e75f3 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/login/LoginNavigation.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/login/LoginNavigation.kt @@ -8,7 +8,9 @@ import kotlinx.serialization.Serializable fun NavController.navigateLogin() { navigate( route = LoginRoute, - ) + ) { + launchSingleTop = true + } } fun NavGraphBuilder.loginNavGraph( diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/my/bbangzipdetail/navigation/BbangZipDetailNavigation.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/my/bbangzipdetail/navigation/BbangZipDetailNavigation.kt index 9918cd2a..eea9b085 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/my/bbangzipdetail/navigation/BbangZipDetailNavigation.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/my/bbangzipdetail/navigation/BbangZipDetailNavigation.kt @@ -9,7 +9,9 @@ import org.android.bbangzip.presentation.ui.my.bbangzipdetail.BbangZipDetailRout fun NavController.navigateBbangZipDetail() { navigate( route = BbangZipDetailRoute, - ) + ) { + launchSingleTop = true + } } fun NavGraphBuilder.bbangZipDetailNavGraph( diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/my/mybadgecategory/navigation/MyBadgeCategoryNavigation.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/my/mybadgecategory/navigation/MyBadgeCategoryNavigation.kt index cd2f501a..a6fe8afb 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/my/mybadgecategory/navigation/MyBadgeCategoryNavigation.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/my/mybadgecategory/navigation/MyBadgeCategoryNavigation.kt @@ -12,7 +12,9 @@ object MyBadgeCategoryRoute fun NavController.navigateToMyBadgeCategory() { navigate( route = MyBadgeCategoryRoute, - ) + ) { + launchSingleTop = true + } } fun NavGraphBuilder.myBadgeCategoryNavGraph( diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/navigator/MainNavHost.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/navigator/MainNavHost.kt index 7c969123..8c0ddb24 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/navigator/MainNavHost.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/navigator/MainNavHost.kt @@ -71,10 +71,12 @@ fun MainNavHost( onboardingNavGraph( navigateToOnboardingEnd = { navigator.navigateToOnboardingEnd() }, + popBackStack = { navigator.popBackStackIfNotSubject() }, ) onboardingEndNavGraph( navigateToSubject = { navigator.navigateToSubject() }, + popBackStack = { navigator.popBackStackIfNotSubject() }, ) todoAddNavGraph( @@ -115,6 +117,7 @@ fun MainNavHost( addStudyNavGraph( padding = padding, + popBackStack = { navigator.popBackStackIfNotSubject() }, navigateSplitStudy = { navigator.navigateToSplitStudy(it) }, ) @@ -142,9 +145,10 @@ fun MainNavHost( navigateToAddToDo = { navigator.navigateToToDoAdd() }, navigateToAddPendingToDo = { navigator.navigateToToDoAddPending() }, ) + subjectDetailNavGraph( padding = padding, - navigateToBack = { navigator.popBackStackIfNotSubject() }, + popBackStack = { navigator.popBackStackIfNotSubject() }, navigateToModifyMotivation = { id, name -> navigator.navigateToModifyMotivationMessage(id, name) }, navigateToModifySubjectName = { id, name -> navigator.navigateToModifySubjectName(id, name) }, navigateToAddStudy = { splitStudyData -> navigator.navigateToAddStudy(splitStudyData) }, diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/navigator/MainNavigator.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/navigator/MainNavigator.kt index 6c34a1f4..31284d0d 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/navigator/MainNavigator.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/navigator/MainNavigator.kt @@ -4,7 +4,9 @@ import android.annotation.SuppressLint import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.navigation.NavDestination +import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController +import androidx.navigation.NavOptions import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import androidx.navigation.navOptions @@ -62,7 +64,7 @@ class MainNavigator( Timber.d("[navigation] restoreState -> $restoreState") }.let { navOptions -> when (bottomNavigationType) { - BottomNavigationType.SUBJECT -> navHostController.navigateSubject() + BottomNavigationType.SUBJECT -> navHostController.navigateSubject(navOptions) BottomNavigationType.TODO -> navHostController.navigateTodo(navOptions) BottomNavigationType.FRIEND -> navHostController.navigateFriend(navOptions) BottomNavigationType.MY -> navHostController.navigateMy(navOptions) @@ -82,8 +84,15 @@ class MainNavigator( navHostController.navigateLogin() } - fun navigateToSubject() { - navHostController.navigateSubject() + fun navigateToSubject(navOptions: NavOptions? = null) { + navHostController.navigateSubject( + navOptions ?: navOptions { + popUpTo(navHostController.graph.findStartDestination().id) { + inclusive = true + } + launchSingleTop = true + }, + ) } fun navigateToAddStudy(splitStudyData: SplitStudyData) { diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/OnboardingContract.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/OnboardingContract.kt index b2fb7d07..0a304e87 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/OnboardingContract.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/OnboardingContract.kt @@ -26,6 +26,8 @@ class OnboardingContract { } sealed interface OnboardingEvent : BaseContract.Event { + data object Initialize : OnboardingEvent + data class OnChangeUserName(val userName: String) : OnboardingEvent data class OnChangeUserNameFocused(val isFocused: Boolean) : OnboardingEvent @@ -44,8 +46,12 @@ class OnboardingContract { data object OnClickBackBtn : OnboardingEvent + data object OnClickBackFromEndBtn : OnboardingEvent + data object OnClickNextBtn : OnboardingEvent + data object OnClickOnboardingNextBtn : OnboardingEvent + data object OnClickFinishBtn : OnboardingEvent } diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/OnboardingViewModel.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/OnboardingViewModel.kt index 20bc312c..de6efcac 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/OnboardingViewModel.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/OnboardingViewModel.kt @@ -4,14 +4,18 @@ import android.os.Parcelable import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import org.android.bbangzip.UserPreferences import org.android.bbangzip.domain.model.OnboardingEntity import org.android.bbangzip.domain.repository.local.UserLocalRepository import org.android.bbangzip.domain.usecase.PostOnboardingUseCase import org.android.bbangzip.presentation.model.BbangZipTextFieldInputState +import org.android.bbangzip.presentation.model.Semester import org.android.bbangzip.presentation.type.SemesterType import org.android.bbangzip.presentation.util.base.BaseViewModel import timber.log.Timber @@ -27,6 +31,8 @@ class OnboardingViewModel ) : BaseViewModel( savedStateHandle = savedStateHandle, ) { + private val userPreferencesFlow: Flow = userLocalRepository.userPreferenceFlow + private fun setUserOnboardingInfo( userName: String, year: Int, @@ -40,8 +46,14 @@ class OnboardingViewModel return savedState as? OnboardingContract.OnboardingState ?: OnboardingContract.OnboardingState() } + init { + setEvent(OnboardingContract.OnboardingEvent.Initialize) + } + override fun handleEvent(event: OnboardingContract.OnboardingEvent) { when (event) { + is OnboardingContract.OnboardingEvent.Initialize -> launch { initDataLoad() } + is OnboardingContract.OnboardingEvent.OnChangeUserName -> { updateState( OnboardingContract.OnboardingReduce.UpdateUserName( @@ -125,31 +137,24 @@ class OnboardingViewModel is OnboardingContract.OnboardingEvent.OnClickBackBtn -> { val previousPage = currentUiState.currentPage - 1 when (currentUiState.currentPage) { + 0 -> { + setSideEffect(OnboardingContract.OnboardingSideEffect.PopBackStack) + } + 1 -> { updateState(OnboardingContract.OnboardingReduce.UpdateCurrentPage(nextPage = previousPage)) - setSideEffect(OnboardingContract.OnboardingSideEffect.NavigateToOnboardingStart) } 2 -> updateState(OnboardingContract.OnboardingReduce.UpdateCurrentPage(nextPage = previousPage)) - 3 -> updateState(OnboardingContract.OnboardingReduce.UpdateCurrentPage(nextPage = previousPage)) - 4 -> { - updateState(OnboardingContract.OnboardingReduce.UpdateCurrentPage(nextPage = previousPage)) - setSideEffect(OnboardingContract.OnboardingSideEffect.NavigateToOnboarding) - } } } is OnboardingContract.OnboardingEvent.OnClickNextBtn -> { val nextPage = currentUiState.currentPage + 1 when (currentUiState.currentPage) { - 0 -> { - updateState(OnboardingContract.OnboardingReduce.UpdateCurrentPage(nextPage = nextPage)) - setSideEffect(OnboardingContract.OnboardingSideEffect.NavigateToOnboarding) - } - + 0 -> updateState(OnboardingContract.OnboardingReduce.UpdateCurrentPage(nextPage = nextPage)) 1 -> updateState(OnboardingContract.OnboardingReduce.UpdateCurrentPage(nextPage = nextPage)) 2 -> { - updateState(OnboardingContract.OnboardingReduce.UpdateCurrentPage(nextPage = nextPage)) Timber.d("[온보딩] -> ${currentUiState.userName}, ${currentUiState.semester}, ${currentUiState.subjectName}") setUserOnboardingInfo( userName = currentUiState.userName ?: "", @@ -157,6 +162,11 @@ class OnboardingViewModel semester = currentUiState.semester.semester.text, subject = currentUiState.subjectName ?: "", ) + setIsOnboardingDonePreferences(isOnboardingDone = true) + viewModelScope.launch { + Timber.tag("[온보딩] isOnboardingDone true로 설정").d(userPreferencesFlow.first().isOnboardingDone.toString()) + Timber.tag("[온보딩] isOnboardingDone true로 설정").d(currentUiState.currentPage.toString()) + } setSideEffect(OnboardingContract.OnboardingSideEffect.NavigateToOnboardingEnd) } } @@ -167,6 +177,14 @@ class OnboardingViewModel Timber.d("[온보딩] -> $onboardingInfo") postOnboardingInfo(onboardingInfo = onboardingInfo) } + + is OnboardingContract.OnboardingEvent.OnClickOnboardingNextBtn -> { + setSideEffect(OnboardingContract.OnboardingSideEffect.NavigateToOnboarding) + } + + is OnboardingContract.OnboardingEvent.OnClickBackFromEndBtn -> { + setSideEffect(OnboardingContract.OnboardingSideEffect.PopBackStack) + } } } @@ -227,6 +245,43 @@ class OnboardingViewModel } } + private fun initDataLoad() { + viewModelScope.launch { + val isOnboardingDone = getInitialIsOnboardingDonePreferences() + val onboardingInfo = getInitialOnboardingInfoPreferences() + + Timber.tag("[온보딩] isOnboardingDone 검사").d(userPreferencesFlow.first().isOnboardingDone.toString()) + + if (isOnboardingDone) { + updateState(OnboardingContract.OnboardingReduce.UpdateUserName(onboardingInfo.userName)) + updateState( + OnboardingContract.OnboardingReduce.UpdateSemester( + Semester( + year = onboardingInfo.year.toString(), + semester = SemesterType.entries.find { it.text == onboardingInfo.semester } ?: SemesterType.FIRST, + ), + ), + ) + updateState(OnboardingContract.OnboardingReduce.UpdateSubject(onboardingInfo.subjectName)) + updateState(OnboardingContract.OnboardingReduce.UpdateCurrentPage(nextPage = 2)) + } else { + updateState(OnboardingContract.OnboardingReduce.UpdateCurrentPage(nextPage = 0)) + } + + setIsOnboardingDonePreferences(isOnboardingDone = false) + } + } + + private fun setIsOnboardingDonePreferences(isOnboardingDone: Boolean) { + viewModelScope.launch { + userLocalRepository.setIsOnOnboardingDone(isOnboardingDone = isOnboardingDone) + } + } + + private suspend fun getInitialIsOnboardingDonePreferences() = userPreferencesFlow.first().isOnboardingDone + + private suspend fun getInitialOnboardingInfoPreferences() = userPreferencesFlow.first().onboardingInfo + private fun determineTextFieldType( text: String, isFocused: Boolean, diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/navigation/OnboardingEndNavigation.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/navigation/OnboardingEndNavigation.kt index 802eebf6..070c81b7 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/navigation/OnboardingEndNavigation.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/navigation/OnboardingEndNavigation.kt @@ -13,10 +13,12 @@ fun NavController.navigateOnboardingEnd() { } fun NavGraphBuilder.onboardingEndNavGraph( + popBackStack: () -> Unit, navigateToSubject: () -> Unit, ) { composable { OnboardingEndRoute( + popBackStack = popBackStack, navigateToSubject = navigateToSubject, ) } diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/navigation/OnboardingNavigation.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/navigation/OnboardingNavigation.kt index 9cf8ca76..1640fda1 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/navigation/OnboardingNavigation.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/navigation/OnboardingNavigation.kt @@ -13,10 +13,12 @@ fun NavController.navigateOnboarding() { } fun NavGraphBuilder.onboardingNavGraph( + popBackStack: () -> Unit, navigateToOnboardingEnd: () -> Unit, ) { composable { OnboardingRoute( + popBackStack = popBackStack, navigateToOnboardingEnd = navigateToOnboardingEnd, ) } diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboarding/OnboardingRoute.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboarding/OnboardingRoute.kt index 9680b471..48f142a5 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboarding/OnboardingRoute.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboarding/OnboardingRoute.kt @@ -5,6 +5,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LifecycleEventEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle import kotlinx.coroutines.flow.collectLatest import org.android.bbangzip.presentation.ui.onboarding.OnboardingContract @@ -13,15 +15,22 @@ import timber.log.Timber @Composable fun OnboardingRoute( + popBackStack: () -> Unit, navigateToOnboardingEnd: () -> Unit, viewModel: OnboardingViewModel = hiltViewModel(), ) { val state by viewModel.uiState.collectAsStateWithLifecycle() val pagerState = rememberPagerState(pageCount = { 3 }) + LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { + Timber.tag("[온보딩]").d("isOnboardingDone 검사 ${state.currentPage}") + viewModel.setEvent(OnboardingContract.OnboardingEvent.Initialize) + } + LaunchedEffect(viewModel.uiSideEffect) { viewModel.uiSideEffect.collectLatest { when (it) { + is OnboardingContract.OnboardingSideEffect.PopBackStack -> popBackStack() is OnboardingContract.OnboardingSideEffect.NavigateToOnboardingEnd -> navigateToOnboardingEnd() else -> Unit } @@ -29,16 +38,12 @@ fun OnboardingRoute( } LaunchedEffect(state.currentPage) { - Timber.d("[온보딩] currentPage -> ${state.currentPage}") + Timber.tag("[온보딩] currentPage").d("${state.currentPage} + ${pagerState.currentPage}") if (pagerState.currentPage != state.currentPage) { pagerState.animateScrollToPage(state.currentPage) } } - LaunchedEffect(pagerState.currentPage) { - viewModel.setEvent(OnboardingContract.OnboardingEvent.OnChangeCurrentPage(pagerState.currentPage)) - } - OnboardingScreen( state = state, pagerState = pagerState, diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboarding/OnboardingScreen.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboarding/OnboardingScreen.kt index fc06c83b..a2780f6a 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboarding/OnboardingScreen.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboarding/OnboardingScreen.kt @@ -1,5 +1,6 @@ package org.android.bbangzip.presentation.ui.onboarding.onboarding +import android.app.Activity import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -15,7 +16,9 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusManager +import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -49,6 +52,7 @@ fun OnboardingScreen( clearUserName: () -> Unit = {}, clearSubject: () -> Unit = {}, ) { + (LocalView.current.context as Activity).window.statusBarColor = BbangZipTheme.colors.backgroundNormal_FFFFFF.toArgb() val focusManager = LocalFocusManager.current Column( diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingend/OnboardingEndRoute.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingend/OnboardingEndRoute.kt index cb73693c..929e47b2 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingend/OnboardingEndRoute.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingend/OnboardingEndRoute.kt @@ -9,12 +9,14 @@ import org.android.bbangzip.presentation.ui.onboarding.OnboardingViewModel @Composable fun OnboardingEndRoute( + popBackStack: () -> Unit, navigateToSubject: () -> Unit, viewModel: OnboardingViewModel = hiltViewModel(), ) { LaunchedEffect(viewModel.uiSideEffect) { viewModel.uiSideEffect.collectLatest { when (it) { + is OnboardingContract.OnboardingSideEffect.PopBackStack -> popBackStack() is OnboardingContract.OnboardingSideEffect.NavigateToSubject -> navigateToSubject() else -> Unit } @@ -23,6 +25,6 @@ fun OnboardingEndRoute( OnboardingEndScreen( onClickNextBtn = { viewModel.setEvent(OnboardingContract.OnboardingEvent.OnClickFinishBtn) }, - onBackBtnClick = { viewModel.setEvent(OnboardingContract.OnboardingEvent.OnClickBackBtn) }, + onBackBtnClick = { viewModel.setEvent(OnboardingContract.OnboardingEvent.OnClickBackFromEndBtn) }, ) } diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingend/OnboardingEndScreen.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingend/OnboardingEndScreen.kt index a7ac1aaa..a5235b21 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingend/OnboardingEndScreen.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingend/OnboardingEndScreen.kt @@ -1,5 +1,6 @@ package org.android.bbangzip.presentation.ui.onboarding.onboardingend +import android.app.Activity import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column @@ -11,7 +12,9 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -28,6 +31,8 @@ fun OnboardingEndScreen( onClickNextBtn: () -> Unit = {}, onBackBtnClick: () -> Unit = {}, ) { + (LocalView.current.context as Activity).window.statusBarColor = BbangZipTheme.colors.backgroundNormal_FFFFFF.toArgb() + Column( modifier = Modifier diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingstart/OnboardingStartRoute.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingstart/OnboardingStartRoute.kt index ed502c8f..f5fa8f94 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingstart/OnboardingStartRoute.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingstart/OnboardingStartRoute.kt @@ -22,6 +22,6 @@ fun OnboardingStartRoute( } OnboardingStartScreen( - onClickNextBtn = { viewModel.setEvent(OnboardingContract.OnboardingEvent.OnClickNextBtn) }, + onClickNextBtn = { viewModel.setEvent(OnboardingContract.OnboardingEvent.OnClickOnboardingNextBtn) }, ) } diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingstart/OnboardingStartScreen.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingstart/OnboardingStartScreen.kt index 0f994e1f..29cdf512 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingstart/OnboardingStartScreen.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/onboarding/onboardingstart/OnboardingStartScreen.kt @@ -1,5 +1,6 @@ package org.android.bbangzip.presentation.ui.onboarding.onboardingstart +import android.app.Activity import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -11,7 +12,9 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -27,6 +30,8 @@ import org.android.bbangzip.ui.theme.BbangZipTheme fun OnboardingStartScreen( onClickNextBtn: () -> Unit = {}, ) { + (LocalView.current.context as Activity).window.statusBarColor = BbangZipTheme.colors.backgroundNormal_FFFFFF.toArgb() + Column( modifier = Modifier diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/splash/SplashViewModel.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/splash/SplashViewModel.kt index 09eb240b..b95e1158 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/splash/SplashViewModel.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/splash/SplashViewModel.kt @@ -52,7 +52,7 @@ class SplashViewModel val isLogin = getInitialIsLoginPreferences() val isOnboardingCompleted = getInitialInOnboardingPreferences() - delay(2000L) + delay(20L) if (isLogin) { if (isOnboardingCompleted) { diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/SubjectNavigation.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/SubjectNavigation.kt index f7a72e3b..69bf9518 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/SubjectNavigation.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/SubjectNavigation.kt @@ -3,13 +3,15 @@ package org.android.bbangzip.presentation.ui.subject import androidx.compose.foundation.layout.PaddingValues import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions import androidx.navigation.compose.composable import org.android.bbangzip.presentation.model.BottomNavigationRoute import org.android.bbangzip.presentation.model.SplitStudyData -fun NavController.navigateSubject() { +fun NavController.navigateSubject(navOptions: NavOptions) { navigate( route = BottomNavigationRoute.Subject, + navOptions = navOptions, ) } diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyNavigation.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyNavigation.kt index 4be453a6..fff7c241 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyNavigation.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyNavigation.kt @@ -24,6 +24,7 @@ fun NavController.navigateAddStudy( fun NavGraphBuilder.addStudyNavGraph( padding: PaddingValues, + popBackStack: () -> Unit, navigateSplitStudy: (AddStudyData) -> Unit, ) { composable( @@ -31,6 +32,7 @@ fun NavGraphBuilder.addStudyNavGraph( ) { AddStudyRoute( padding = padding, + popBackStack = popBackStack, navigateSplitStudy = navigateSplitStudy, splitStudyData = it.toRoute().splitStudyData, ) diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyRoute.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyRoute.kt index 6e237485..30bd178a 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyRoute.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyRoute.kt @@ -13,6 +13,7 @@ import org.android.bbangzip.presentation.model.SplitStudyData @Composable fun AddStudyRoute( padding: PaddingValues, + popBackStack: () -> Unit, splitStudyData: SplitStudyData, viewModel: AddStudyViewModel = hiltViewModel(), navigateSplitStudy: (AddStudyData) -> Unit = {}, diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyScreen.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyScreen.kt index cc425d0a..8d106112 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyScreen.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyScreen.kt @@ -94,6 +94,7 @@ fun AddStudyScreen( leadingIcon = R.drawable.ic_chevronleft_thick_small_24, title = subjectTitle, ) + Column( modifier = Modifier diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyViewModel.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyViewModel.kt index 148d68ae..0dcec8b6 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyViewModel.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/addstudy/AddStudyViewModel.kt @@ -79,7 +79,8 @@ class AddStudyViewModel updateState(AddStudyReduce.UpdateSelectedDate(date = event.selectedDate)) } - AddStudyContract.AddStudyEvent.OnClickBackIcon -> { } + AddStudyContract.AddStudyEvent.OnClickBackIcon -> { + } AddStudyContract.AddStudyEvent.OnClickDatePicker -> { updateState(AddStudyReduce.UpdateDatePickerBottomSheetState) } diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailContract.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailContract.kt index e2ccbe57..990a49ce 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailContract.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailContract.kt @@ -3,6 +3,7 @@ package org.android.bbangzip.presentation.ui.subject.subjectdetail import android.os.Parcelable import kotlinx.parcelize.Parcelize import org.android.bbangzip.presentation.component.card.BbangZipCardState +import org.android.bbangzip.presentation.model.Badge import org.android.bbangzip.presentation.model.SplitStudyData import org.android.bbangzip.presentation.model.SubjectDetailInfo import org.android.bbangzip.presentation.model.card.ToDoCardModel @@ -138,6 +139,8 @@ class SubjectDetailContract { BbangZipCardState.DEFAULT, ), ), + val badgeList: List = emptyList(), + val getBadgeBottomSheetState: Boolean = false, ) : BaseContract.State, Parcelable { override fun toParcelable(): Parcelable = this } @@ -187,6 +190,10 @@ class SubjectDetailContract { data class OnCompleteCardClicked( val pieceId: Int, ) : SubjectDetailEvent + + data object OnClickGetBadgeBottomSheetCloseBtn : SubjectDetailEvent + + data object OnClickBackIconBtn : SubjectDetailEvent } sealed interface SubjectDetailReduce : BaseContract.Reduce { @@ -218,6 +225,10 @@ class SubjectDetailContract { data object UpdateIsMenuOpen : SubjectDetailReduce data class UpdateExamName(val index: Int) : SubjectDetailReduce + + data class UpdateGetBadgeList(val badgeList: List) : SubjectDetailReduce + + data class UpdateGetBadgeBottomSheetState(val getBadgeBottomSheetState: Boolean) : SubjectDetailReduce } sealed interface SubjectDetailSideEffect : BaseContract.SideEffect { @@ -231,5 +242,7 @@ class SubjectDetailContract { // 공부 n개가 삭제 되었어요 data object ShowDeleteSuccessSnackBar : SubjectDetailSideEffect + + data object PopBackStack : SubjectDetailSideEffect } } diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailNavigation.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailNavigation.kt index 7aa7b733..42d9cf3e 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailNavigation.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailNavigation.kt @@ -23,7 +23,7 @@ fun NavController.navigateToSubjectDetail( fun NavGraphBuilder.subjectDetailNavGraph( padding: PaddingValues, - navigateToBack: () -> Unit = {}, + popBackStack: () -> Unit = {}, navigateToModifyMotivation: (Int, String) -> Unit = { _, _ -> }, navigateToModifySubjectName: (Int, String) -> Unit = { _, _ -> }, navigateToAddStudy: (SplitStudyData) -> Unit = {}, @@ -33,7 +33,7 @@ fun NavGraphBuilder.subjectDetailNavGraph( padding = padding, subjectId = backStackEntry.toRoute().subjectId, subjectName = backStackEntry.toRoute().subjectName, - navigateToBack = navigateToBack, + popBackStack = popBackStack, navigateToModifyMotivation = navigateToModifyMotivation, navigateToModifySubjectName = navigateToModifySubjectName, navigateToAddStudy = navigateToAddStudy, diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailRoute.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailRoute.kt index 1e9706a3..2e44b776 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailRoute.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailRoute.kt @@ -21,7 +21,7 @@ fun SubjectDetailRoute( padding: PaddingValues, subjectId: Int, subjectName: String, - navigateToBack: () -> Unit, + popBackStack: () -> Unit, navigateToModifyMotivation: (Int, String) -> Unit, navigateToModifySubjectName: (Int, String) -> Unit, navigateToAddStudy: (SplitStudyData) -> Unit, @@ -52,42 +52,19 @@ fun SubjectDetailRoute( is SubjectDetailContract.SubjectDetailSideEffect.NavigateToModifySubjectName -> { navigateToModifySubjectName(effect.subjectId, effect.subjectName) } + + is SubjectDetailContract.SubjectDetailSideEffect.PopBackStack -> { + popBackStack() + } } } } - SubjectDetailScreen( - padding = padding, - isMenuOpen = subjectDetailState.isMenuOpen, - todoList = subjectDetailState.todoList, - pieceViewType = subjectDetailState.pieceViewType, - deletedSet = subjectDetailState.selectedItemSet, - revertCompleteBottomSheetState = subjectDetailState.revertCompleteBottomSheetState, - selectedItemId = subjectDetailState.selectedItemId, - subjectId = subjectDetailState.subjectId, - subjectName = subjectDetailState.subjectName, - motivationMessage = subjectDetailState.motivationMessage, - examDDay = subjectDetailState.examDday, - examDate = subjectDetailState.examDate, - examName = subjectDetailState.examName, - onClickTab = { index -> viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnClickTab(index)) }, - onCloseIconClicked = { viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnCloseIconClicked) }, - onTrashIconClicked = { viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnTrashIconClicked) }, - onDeleteModeCardClicked = { id -> viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnDeleteModeCardClicked(id)) }, - onDefaultCardClicked = { id -> viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnDefaultCardClicked(id)) }, - onCompleteCardClicked = { id -> viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnCompleteCardClicked(id)) }, - onClickEnrollMotivationMessage = { id, name -> viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnClickEnrollMotivateMessage(id, name)) }, - onClickModifySubjectName = { id, name -> viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnClickModifySubjectName(id, name)) }, - onClickKebabMenu = { viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnClickKebabMenu) }, - onClickAddStudy = { splitStudyData -> viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnPlusIconClicked(splitStudyData)) }, - onRevertCompleteBottomSheetDismissRequest = { viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnRevertCompleteBottomSheetDissmissRequest) }, - onRevertCompleteBottomSheetApproveButtonClicked = { pieceId -> viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnRevertCompleteBottomSheetApproveButtonClicked(pieceId = pieceId)) }, - onRevertCompleteBottomSheetDismissButtonClicked = { viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnRevertCompleteBottomSheetDismissButtonClicked) }, - ) when (success) { true -> SubjectDetailScreen( padding = padding, + state = subjectDetailState, isMenuOpen = subjectDetailState.isMenuOpen, todoList = subjectDetailState.todoList, pieceViewType = subjectDetailState.pieceViewType, @@ -112,6 +89,8 @@ fun SubjectDetailRoute( onRevertCompleteBottomSheetDismissRequest = { viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnRevertCompleteBottomSheetDissmissRequest) }, onRevertCompleteBottomSheetApproveButtonClicked = { pieceId -> viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnRevertCompleteBottomSheetApproveButtonClicked(pieceId = pieceId)) }, onRevertCompleteBottomSheetDismissButtonClicked = { viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnRevertCompleteBottomSheetDismissButtonClicked) }, + onClickBadgeCloseBtn = { viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnClickGetBadgeBottomSheetCloseBtn) }, + popBackStack = { viewModel.setEvent(SubjectDetailContract.SubjectDetailEvent.OnClickBackIconBtn) }, ) false -> Box( diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailViewModel.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailViewModel.kt index 1b41b2c3..dcff9e49 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailViewModel.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetailViewModel.kt @@ -102,6 +102,14 @@ class SubjectDetailViewModel is SubjectDetailContract.SubjectDetailEvent.OnClickTab -> { updateState(SubjectDetailContract.SubjectDetailReduce.UpdateExamName(event.index)) } + + is SubjectDetailContract.SubjectDetailEvent.OnClickGetBadgeBottomSheetCloseBtn -> { + updateState(SubjectDetailContract.SubjectDetailReduce.UpdateGetBadgeBottomSheetState(getBadgeBottomSheetState = false)) + } + + is SubjectDetailContract.SubjectDetailEvent.OnClickBackIconBtn -> { + setSideEffect(SubjectDetailContract.SubjectDetailSideEffect.PopBackStack) + } } } @@ -240,6 +248,18 @@ class SubjectDetailViewModel examName = if (reduce.index == 0) "중간고사" else "기말고사", ) } + + is SubjectDetailContract.SubjectDetailReduce.UpdateGetBadgeList -> { + state.copy( + badgeList = reduce.badgeList, + ) + } + + is SubjectDetailContract.SubjectDetailReduce.UpdateGetBadgeBottomSheetState -> { + state.copy( + getBadgeBottomSheetState = !currentUiState.getBadgeBottomSheetState, + ) + } } } @@ -306,7 +326,9 @@ class SubjectDetailViewModel postCompleteCardIdUseCase( pieceId = pieceId, requestMarkDoneDto = RequestMarkDoneDto(isFinished = true), - ).onSuccess { + ).onSuccess { data -> + updateState(SubjectDetailContract.SubjectDetailReduce.UpdateGetBadgeList(badgeList = data.badgeCardList.map { it.toBadge() })) + updateState(SubjectDetailContract.SubjectDetailReduce.UpdateGetBadgeBottomSheetState(getBadgeBottomSheetState = true)) Timber.tag("markDone").e("완료 성공!") }.onFailure { error -> Timber.tag("markDone").e(error) diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetatilScreen.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetatilScreen.kt index 4606caf2..ef2d3c73 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetatilScreen.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/subject/subjectdetail/SubjectDetatilScreen.kt @@ -47,6 +47,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.android.bbangzip.R import org.android.bbangzip.presentation.component.bottomsheet.BbangZipBasicModalBottomSheet +import org.android.bbangzip.presentation.component.bottomsheet.BbangZipGetBadgeBottomSheet import org.android.bbangzip.presentation.component.button.BbangZipButton import org.android.bbangzip.presentation.component.card.BbangZipCardState import org.android.bbangzip.presentation.component.card.ToDoCard @@ -68,6 +69,7 @@ import timber.log.Timber @Composable fun SubjectDetailScreen( padding: PaddingValues, + state: SubjectDetailContract.SubjectDetailState, isMenuOpen: Boolean, todoList: List, pieceViewType: PieceViewType, @@ -94,6 +96,8 @@ fun SubjectDetailScreen( onClickAddStudy: (SplitStudyData) -> Unit = {}, onDefaultCardClicked: (Int) -> Unit = {}, onCompleteCardClicked: (Int) -> Unit = {}, + onClickBadgeCloseBtn: () -> Unit, + popBackStack: () -> Unit, ) { Timber.tag("김재민").d("SubjectDetailScreen : $subjectName $examName") val configuration = LocalConfiguration.current @@ -237,6 +241,7 @@ fun SubjectDetailScreen( leadingIcon = R.drawable.ic_chevronleft_thick_small_24, trailingIcon = R.drawable.ic_menu_kebab_default_24, onTrailingIconClick = { onClickKebabMenu() }, + onLeadingIconClick = { popBackStack() }, title = subjectName, ) if (isMenuOpen) { @@ -320,6 +325,15 @@ fun SubjectDetailScreen( onClickInteractButton = onRevertCompleteBottomSheetApproveButtonClicked, onClickCancelButton = onRevertCompleteBottomSheetDismissButtonClicked, ) + + if (state.badgeList.isNotEmpty()) { + BbangZipGetBadgeBottomSheet( + badgeList = state.badgeList, + isBottomSheetVisible = state.getBadgeBottomSheetState, + onDismissRequest = { onClickBadgeCloseBtn() }, + onClickCancelButton = { onClickBadgeCloseBtn() }, + ) + } } } @@ -741,6 +755,7 @@ fun RevertCompleteBottomSheet( private fun SubjectDetailScreenPreview() { SubjectDetailScreen( padding = PaddingValues(64.dp), + state = SubjectDetailContract.SubjectDetailState(), todoList = emptyList(), pieceViewType = PieceViewType.DEFAULT, deletedSet = emptySet(), @@ -753,5 +768,7 @@ private fun SubjectDetailScreenPreview() { examDate = "2025년 1월 1일", examDDay = 14, examName = "중간고사", + onClickBadgeCloseBtn = { }, + popBackStack = { }, ) } diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoContract.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoContract.kt index 6a852abc..1dd8853d 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoContract.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoContract.kt @@ -3,6 +3,7 @@ package org.android.bbangzip.presentation.ui.todo import android.os.Parcelable import kotlinx.parcelize.Parcelize import org.android.bbangzip.presentation.component.card.BbangZipCardState +import org.android.bbangzip.presentation.model.Badge import org.android.bbangzip.presentation.model.card.ToDoCardModel import org.android.bbangzip.presentation.type.ToDoFilterType import org.android.bbangzip.presentation.type.ToDoScreenType @@ -132,6 +133,8 @@ class TodoContract { val selectedItemList: List = listOf(), val revertCompleteBottomSheetState: Boolean = false, val screenType: ToDoScreenType = ToDoScreenType.DEFAULT, + val badgeList: List = emptyList(), + val getBadgeBottomSheetState: Boolean = false, ) : BaseContract.State, Parcelable { override fun toParcelable(): Parcelable = this } @@ -183,6 +186,8 @@ class TodoContract { val pieceId: Int, val cardState: BbangZipCardState, ) : TodoEvent + + data object OnClickGetBadgeBottomSheetCloseBtn : TodoEvent } sealed interface TodoReduce : BaseContract.Reduce { @@ -228,6 +233,10 @@ class TodoContract { data class DeleteSelectedItemList(val pieceId: Int) : TodoReduce data object ResetSelectedItemList : TodoReduce + + data class UpdateGetBadgeList(val badgeList: List) : TodoReduce + + data class UpdateGetBadgeBottomSheetState(val getBadgeBottomSheetState: Boolean) : TodoReduce } sealed interface TodoSideEffect : BaseContract.SideEffect { diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoRoute.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoRoute.kt index f98aa4e3..90b38bc3 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoRoute.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoRoute.kt @@ -115,6 +115,9 @@ fun TodoRoute( onDefaultScreenCardClicked = { pieceId, cardState -> viewModel.setEvent(TodoContract.TodoEvent.OnDefaultScreenCardClicked(pieceId = pieceId, cardState = cardState)) }, + onClickBadgeCloseBtn = { + viewModel.setEvent(TodoContract.TodoEvent.OnClickGetBadgeBottomSheetCloseBtn) + }, ) false -> diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoScreen.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoScreen.kt index 2e47ee99..d8223936 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoScreen.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoScreen.kt @@ -40,6 +40,7 @@ import androidx.compose.ui.unit.dp import org.android.bbangzip.R import org.android.bbangzip.presentation.component.balloon.TopTailBalloon import org.android.bbangzip.presentation.component.bottomsheet.BbangZipBasicModalBottomSheet +import org.android.bbangzip.presentation.component.bottomsheet.BbangZipGetBadgeBottomSheet import org.android.bbangzip.presentation.component.button.BbangZipButton import org.android.bbangzip.presentation.component.card.BbangZipCardState import org.android.bbangzip.presentation.component.card.ToDoCard @@ -70,6 +71,7 @@ fun TodoScreen( onItemDeleteButtonClicked: () -> Unit = {}, onDeleteScreenCardClicked: (Int, BbangZipCardState) -> Unit = { _, _ -> }, onDefaultScreenCardClicked: (Int, BbangZipCardState) -> Unit = { _, _ -> }, + onClickBadgeCloseBtn: () -> Unit, ) { Box( modifier = @@ -269,6 +271,15 @@ fun TodoScreen( onSelectedItemChanged = onFilterBottomSheetItemClicked, onDismissRequest = onFilterBottomSheetDismissRequest, ) + + if (todoState.badgeList.isNotEmpty()) { + BbangZipGetBadgeBottomSheet( + badgeList = todoState.badgeList, + isBottomSheetVisible = todoState.getBadgeBottomSheetState, + onDismissRequest = { onClickBadgeCloseBtn() }, + onClickCancelButton = { onClickBadgeCloseBtn() }, + ) + } } } @@ -766,5 +777,6 @@ fun TodoScreenMockPreview() { todoState = mockTodoStates[2], todayDate = listOf("2025", "01", "18"), bottomPadding = PaddingValues(), + onClickBadgeCloseBtn = {}, ) } diff --git a/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoViewModel.kt b/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoViewModel.kt index 015a6253..c0fc8323 100644 --- a/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoViewModel.kt +++ b/app/src/main/java/org/android/bbangzip/presentation/ui/todo/TodoViewModel.kt @@ -223,6 +223,8 @@ class TodoViewModel // 화면 이동 TodoContract.TodoEvent.OnAddStudyButtonClicked -> setSideEffect(TodoContract.TodoSideEffect.NavigateToAddToDo) TodoContract.TodoEvent.OnAddPendingStudyButtonClicked -> setSideEffect(TodoContract.TodoSideEffect.NavigateToAddPendingToDo) + is TodoContract.TodoEvent.OnClickGetBadgeBottomSheetCloseBtn -> + updateState(TodoContract.TodoReduce.UpdateGetBadgeBottomSheetState(getBadgeBottomSheetState = false)) } } @@ -322,6 +324,16 @@ class TodoViewModel state.copy( selectedItemList = listOf(), ) + + is TodoContract.TodoReduce.UpdateGetBadgeList -> + state.copy( + badgeList = reduce.badgeList, + ) + + is TodoContract.TodoReduce.UpdateGetBadgeBottomSheetState -> + state.copy( + getBadgeBottomSheetState = !currentUiState.getBadgeBottomSheetState, + ) } } @@ -434,7 +446,11 @@ class TodoViewModel postCompleteCardIdUseCase( pieceId = pieceId, requestMarkDoneDto = RequestMarkDoneDto(isFinished = true), - ).onSuccess { + ).onSuccess { data -> + updateState( + TodoContract.TodoReduce.UpdateGetBadgeList(badgeList = data.badgeCardList.map { it.toBadge() }), + ) + updateState(TodoContract.TodoReduce.UpdateGetBadgeBottomSheetState(getBadgeBottomSheetState = true)) Timber.tag("markDone").e("완료 성공!") }.onFailure { error -> Timber.tag("markDone").e(error) diff --git a/app/src/main/proto/user_prefs.proto b/app/src/main/proto/user_prefs.proto index 44083d47..abac8606 100644 --- a/app/src/main/proto/user_prefs.proto +++ b/app/src/main/proto/user_prefs.proto @@ -9,6 +9,9 @@ message UserPreferences { bool isLogin = 3; bool isOnboardingCompleted = 4; OnboardingInfo onboardingInfo = 5; + bool isOnboardingDone = 6; + bool isBadgeAvailable = 7; + repeated BadgeInfo badges = 8; } message OnboardingInfo { @@ -16,4 +19,10 @@ message OnboardingInfo { int32 year = 2; string semester = 3; string subjectName = 4; +} + +message BadgeInfo { + string badgeName = 1; + string badgeImage = 2; + repeated string hashTags = 3; } \ No newline at end of file diff --git a/app/src/main/res/drawable-xhdpi/img_app_icon.png b/app/src/main/res/drawable-xhdpi/img_app_icon.png new file mode 100644 index 00000000..c4730f5d Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/img_app_icon.png differ diff --git a/app/src/main/res/drawable-xxhdpi/img_app_icon.png b/app/src/main/res/drawable-xxhdpi/img_app_icon.png new file mode 100644 index 00000000..a39d2706 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/img_app_icon.png differ diff --git a/app/src/main/res/drawable/img_app_icon.png b/app/src/main/res/drawable/img_app_icon.png new file mode 100644 index 00000000..7e4c1241 Binary files /dev/null and b/app/src/main/res/drawable/img_app_icon.png differ diff --git a/app/src/main/res/drawable/img_onboarding_first.xml b/app/src/main/res/drawable/img_onboarding_first.xml new file mode 100644 index 00000000..3d7feef3 --- /dev/null +++ b/app/src/main/res/drawable/img_onboarding_first.xml @@ -0,0 +1,762 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/img_onboarding_second.xml b/app/src/main/res/drawable/img_onboarding_second.xml new file mode 100644 index 00000000..2783d31d --- /dev/null +++ b/app/src/main/res/drawable/img_onboarding_second.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + +