diff --git a/app/src/main/java/com/whyranoid/walkie/KoinModules.kt b/app/src/main/java/com/whyranoid/walkie/KoinModules.kt index e0e2e766..8111fe6b 100644 --- a/app/src/main/java/com/whyranoid/walkie/KoinModules.kt +++ b/app/src/main/java/com/whyranoid/walkie/KoinModules.kt @@ -105,6 +105,7 @@ import com.whyranoid.presentation.viewmodel.UserPostsViewModel import com.whyranoid.presentation.viewmodel.challenge.ChallengeCompleteViewModel import com.whyranoid.presentation.viewmodel.challenge.ChallengeDetailViewModel import com.whyranoid.presentation.viewmodel.challenge.ChallengeExitViewModel +import com.whyranoid.presentation.viewmodel.challenge.ChallengeImageSaveViewModel import com.whyranoid.presentation.viewmodel.challenge.ChallengeMainViewModel import com.whyranoid.walkie.walkiedialog.DialogViewModel import com.whyranoid.walkie.walkiedialog.NetworkInterceptor @@ -125,6 +126,7 @@ val viewModelModule = viewModel { ChallengeDetailViewModel(get(), get(), get()) } viewModel { ChallengeExitViewModel(get(), get()) } viewModel { ChallengeCompleteViewModel(get()) } + viewModel { ChallengeImageSaveViewModel(get()) } viewModel { UserPageViewModel( get(), @@ -174,7 +176,7 @@ val dataSourceModule = single { ChallengeDataSourceImpl(get()) } single { PostDataSourceImpl(get()) } single { UserDataSourceImpl(get()) } - single { AccountDataSourceImpl(get(),get()) } + single { AccountDataSourceImpl(get(), get()) } single { FollowDataSourceImpl(get()) } single { RunningControlDataSourceImpl(get()) } single { CommunityDataSourceImpl(get()) } @@ -185,7 +187,7 @@ val useCaseModule = single { GetNewChallengePreviewsUseCase(get()) } single { GetChallengingPreviewsUseCase(get()) } single { GetChallengeDetailUseCase(get(), get()) } - single { CompleteChallengeUseCase(get(), get())} + single { CompleteChallengeUseCase(get(), get()) } single { GetChallengePreviewsByTypeUseCase(get(), get()) } single { GetTopRankChallengePreviewsUseCase(get()) } single { StartChallengeUseCase(get(), get()) } diff --git a/presentation/src/main/java/com/whyranoid/presentation/component/bar/BasicBackButtonTopBar.kt b/presentation/src/main/java/com/whyranoid/presentation/component/bar/BasicBackButtonTopBar.kt new file mode 100644 index 00000000..96ea2cd4 --- /dev/null +++ b/presentation/src/main/java/com/whyranoid/presentation/component/bar/BasicBackButtonTopBar.kt @@ -0,0 +1,29 @@ +package com.whyranoid.presentation.component.bar + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import com.whyranoid.presentation.R + +@Composable +fun BasicBackButtonTopBar( + onClicked: () -> Unit +) { + Row { + IconButton( + modifier = Modifier + .padding(vertical = 19.dp) + .padding(start = 16.dp), + onClick = { onClicked() }) { + Icon( + painterResource(id = R.drawable.ic_back_arrow), + contentDescription = "back arrow" + ) + } + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/whyranoid/presentation/screens/AppScreen.kt b/presentation/src/main/java/com/whyranoid/presentation/screens/AppScreen.kt index ca1f9a46..21570eff 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/screens/AppScreen.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/screens/AppScreen.kt @@ -35,6 +35,7 @@ import com.whyranoid.presentation.screens.Screen.Companion.bottomNavigationItems import com.whyranoid.presentation.screens.challenge.ChallengeCompleteScreen import com.whyranoid.presentation.screens.challenge.ChallengeDetailScreen import com.whyranoid.presentation.screens.challenge.ChallengeExitScreen +import com.whyranoid.presentation.screens.challenge.ChallengeImageSaveScreen import com.whyranoid.presentation.screens.challenge.ChallengeMainScreen import com.whyranoid.presentation.screens.community.CommentScreen import com.whyranoid.presentation.screens.community.SearchFriendScreen @@ -191,6 +192,15 @@ fun AppScreenContent( ChallengeCompleteScreen(navController, challengeId) } + composable( + Screen.ChallengeImageSaveScreen.route, + Screen.ChallengeImageSaveScreen.arguments, + ) { backStackEntry -> + val arguments = requireNotNull(backStackEntry.arguments) + val challengeId = arguments.getLong("challengeId") + ChallengeImageSaveScreen(navController, challengeId) + } + composable( Screen.UserPageScreen.route, Screen.UserPageScreen.arguments, diff --git a/presentation/src/main/java/com/whyranoid/presentation/screens/Screen.kt b/presentation/src/main/java/com/whyranoid/presentation/screens/Screen.kt index c2be8c7b..216b4a96 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/screens/Screen.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/screens/Screen.kt @@ -96,6 +96,14 @@ sealed class Screen( ), ) + object ChallengeImageSaveScreen : Screen( + route = "ChallengeImageSave/{challengeId}", + arguments = + listOf( + navArgument("challengeId") { type = NavType.LongType }, + ), + ) + object UserPageScreen : Screen( route = "userPage/{$UID_ARGUMENT}/{$NICKNAME_ARGUMENT}/{$IS_FOLLOWING_ARGUMENT}", arguments = diff --git a/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeCompleteScreen.kt b/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeCompleteScreen.kt index 69abe1d3..9ddb6ac8 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeCompleteScreen.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeCompleteScreen.kt @@ -10,6 +10,9 @@ 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.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -18,12 +21,16 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.navigation.NavController import coil.compose.AsyncImage import com.whyranoid.domain.util.EMPTY +import com.whyranoid.presentation.R +import com.whyranoid.presentation.component.bar.BasicBackButtonTopBar import com.whyranoid.presentation.component.button.WalkieNegativeButton import com.whyranoid.presentation.component.button.WalkiePositiveButton +import com.whyranoid.presentation.screens.Screen import com.whyranoid.presentation.theme.WalkieTypography import com.whyranoid.presentation.viewmodel.challenge.ChallengeCompleteViewModel import org.koin.androidx.compose.koinViewModel @@ -42,96 +49,110 @@ fun ChallengeCompleteScreen( viewModel.getChallengeDetail(challengeId) } - Column( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight() - .padding(20.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.SpaceBetween - ) { - + Scaffold( + topBar = { + BasicBackButtonTopBar { + navController.popBackStack() + } + } + ) { paddingValues -> Column( modifier = Modifier - .fillMaxWidth(), + .fillMaxWidth() + .fillMaxHeight() + .padding(20.dp) + .padding(paddingValues), horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceBetween ) { - Text( - modifier = Modifier.padding(top = 126.dp), - text = "챌린지 성공!", - style = WalkieTypography.Title - ) - - Spacer(modifier = Modifier.height(56.dp)) - AsyncImage( - model = state.challenge.getDataOrNull()?.badge?.imageUrl - ?: "https://picsum.photos/250/250 ", contentDescription = "", + Column( modifier = Modifier - // .size(180.dp) - , - contentScale = ContentScale.Crop - ) - - Spacer(modifier = Modifier.height(20.dp)) - - Text( - modifier = Modifier.fillMaxWidth(), - text = state.challenge.getDataOrNull()?.name ?: String.EMPTY, - style = WalkieTypography.Title, - textAlign = androidx.compose.ui.text.style.TextAlign.Center - ) - - Spacer(modifier = Modifier.height(8.dp)) - - Text( - modifier = Modifier.fillMaxWidth(), - text = "뱃지 획득!", - style = WalkieTypography.Title, - textAlign = androidx.compose.ui.text.style.TextAlign.Center - ) - - Spacer(modifier = Modifier.height(10.dp)) - - Text( - modifier = Modifier.fillMaxWidth(), - text = "마이페이지에서 확인해보세요", - style = WalkieTypography.Body1.copy( - color = Color(0xFF989898), + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + modifier = Modifier.padding(top = 126.dp), + text = "챌린지 성공!", + style = WalkieTypography.Title + ) + + Spacer(modifier = Modifier.height(56.dp)) + + AsyncImage( + model = state.challenge.getDataOrNull()?.badge?.imageUrl + ?: "https://picsum.photos/250/250 ", contentDescription = "", + modifier = Modifier + // .size(180.dp) + , + contentScale = ContentScale.Crop + ) + + Spacer(modifier = Modifier.height(20.dp)) + + Text( + modifier = Modifier.fillMaxWidth(), + text = state.challenge.getDataOrNull()?.name ?: String.EMPTY, + style = WalkieTypography.Title, textAlign = androidx.compose.ui.text.style.TextAlign.Center ) - ) - } - Column( - modifier = Modifier - .fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally, - ) { + Spacer(modifier = Modifier.height(8.dp)) - Text(text = "사진으로 저장하시겠어요?") + Text( + modifier = Modifier.fillMaxWidth(), + text = "뱃지 획득!", + style = WalkieTypography.Title, + textAlign = androidx.compose.ui.text.style.TextAlign.Center + ) + + Spacer(modifier = Modifier.height(10.dp)) + + Text( + modifier = Modifier.fillMaxWidth(), + text = "마이페이지에서 확인해보세요", + style = WalkieTypography.Body1.copy( + color = Color(0xFF989898), + textAlign = androidx.compose.ui.text.style.TextAlign.Center + ) + ) + } - Row( + Column( modifier = Modifier - .padding(top = 20.dp) - .padding(horizontal = 20.dp) + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, ) { - Box(modifier = Modifier.weight(.5f)) { - WalkieNegativeButton(text = "아니오") { - navController.popBackStack() - } - } - - Spacer(modifier = Modifier.size(10.dp)) - Box(modifier = Modifier.weight(.5f)) { - WalkiePositiveButton(text = "네 좋아요") { + Text(text = "사진으로 저장하시겠어요?") + + Row( + modifier = Modifier + .padding(top = 20.dp) + .padding(horizontal = 20.dp) + ) { + Box(modifier = Modifier.weight(.5f)) { + WalkieNegativeButton(text = "아니오") { + navController.popBackStack() + } + } + Spacer(modifier = Modifier.size(10.dp)) + + Box(modifier = Modifier.weight(.5f)) { + WalkiePositiveButton(text = "네 좋아요") { + navController.navigate( + Screen.ChallengeImageSaveScreen.route.replace( + "{challengeId}", + challengeId.toString() + ) + ) + } } } } - } + } } } \ No newline at end of file diff --git a/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeImageSaveScreen.kt b/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeImageSaveScreen.kt new file mode 100644 index 00000000..1c298fc4 --- /dev/null +++ b/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeImageSaveScreen.kt @@ -0,0 +1,110 @@ +package com.whyranoid.presentation.screens.challenge + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import coil.compose.AsyncImage +import com.whyranoid.domain.util.EMPTY +import com.whyranoid.domain.util.getToday +import com.whyranoid.presentation.R +import com.whyranoid.presentation.component.bar.BasicBackButtonTopBar +import com.whyranoid.presentation.theme.WalkieTypography +import com.whyranoid.presentation.viewmodel.challenge.ChallengeImageSaveViewModel +import org.koin.androidx.compose.koinViewModel +import org.orbitmvi.orbit.compose.collectAsState + +@Composable +fun ChallengeImageSaveScreen( + navController: NavController, + challengeId: Long, +) { + + val viewModel = koinViewModel() + val state by viewModel.collectAsState() + + LaunchedEffect(Unit) { + viewModel.getChallengeDetail(challengeId) + } + Scaffold( + topBar = { + BasicBackButtonTopBar { + navController.popBackStack() + } + } + ) { paddingValues -> + Column( + modifier = Modifier, + ) { + + Spacer(modifier = Modifier.size(118.dp)) + + Box( + modifier = Modifier + .fillMaxWidth() + .padding(paddingValues), + contentAlignment = Alignment.Center, + ) { + Box( + modifier = Modifier, + contentAlignment = Alignment.Center, + ) { + + Image( + modifier = Modifier + .size(300.dp) + .padding(20.dp), + painter = painterResource(id = R.drawable.bg_badge), + contentDescription = "Badge background" + ) + + Column( + modifier = Modifier, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + AsyncImage( + model = state.challenge.getDataOrNull()?.badge?.imageUrl, + contentDescription = "challenge badge image" + ) + + Spacer(modifier = Modifier.size(13.dp)) + + Text( + text = state.challenge.getDataOrNull()?.name ?: String.EMPTY, + style = WalkieTypography.Title + ) + } + + Box( + modifier = Modifier.matchParentSize(), + contentAlignment = Alignment.BottomEnd + ) { + Text( + modifier = Modifier + .padding(end = 30.dp, bottom = 30.dp), + text = getToday().split(" ").first().replace("-", "."), + style = WalkieTypography.Body2 + ) + } + + } + } + } + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/whyranoid/presentation/viewmodel/challenge/ChallengeImageSaveViewModel.kt b/presentation/src/main/java/com/whyranoid/presentation/viewmodel/challenge/ChallengeImageSaveViewModel.kt new file mode 100644 index 00000000..b3912f77 --- /dev/null +++ b/presentation/src/main/java/com/whyranoid/presentation/viewmodel/challenge/ChallengeImageSaveViewModel.kt @@ -0,0 +1,38 @@ +package com.whyranoid.presentation.viewmodel.challenge + +import androidx.lifecycle.ViewModel +import com.whyranoid.domain.model.challenge.Challenge +import com.whyranoid.domain.usecase.GetChallengeDetailUseCase +import com.whyranoid.presentation.model.UiState +import org.orbitmvi.orbit.ContainerHost +import org.orbitmvi.orbit.syntax.simple.intent +import org.orbitmvi.orbit.syntax.simple.reduce +import org.orbitmvi.orbit.viewmodel.container + +sealed class ChallengeImageSaveSideEffect { + +} + +data class ChallengeImageSaveState( + val challenge: UiState = UiState.Idle, +) + +class ChallengeImageSaveViewModel( + private val getChallengeDetailUseCase: GetChallengeDetailUseCase +) : ViewModel(), ContainerHost { + + override val container = + container(ChallengeImageSaveState()) + + fun getChallengeDetail(challengeId: Long) = intent { + reduce { + state.copy(challenge = UiState.Loading) + } + + val challenge = getChallengeDetailUseCase(challengeId) + + reduce { + state.copy(challenge = UiState.Success(challenge)) + } + } +} \ No newline at end of file diff --git a/presentation/src/main/res/drawable/bg_badge.png b/presentation/src/main/res/drawable/bg_badge.png new file mode 100644 index 00000000..663a82ee Binary files /dev/null and b/presentation/src/main/res/drawable/bg_badge.png differ diff --git a/presentation/src/main/res/drawable/ic_left_arrow.xml b/presentation/src/main/res/drawable/ic_left_arrow.xml new file mode 100644 index 00000000..142d0f8a --- /dev/null +++ b/presentation/src/main/res/drawable/ic_left_arrow.xml @@ -0,0 +1,9 @@ + + +