diff --git a/app/src/main/java/com/whyranoid/walkie/KoinModules.kt b/app/src/main/java/com/whyranoid/walkie/KoinModules.kt index 7111fb43..ac6d9f12 100644 --- a/app/src/main/java/com/whyranoid/walkie/KoinModules.kt +++ b/app/src/main/java/com/whyranoid/walkie/KoinModules.kt @@ -120,7 +120,7 @@ import java.util.concurrent.TimeUnit val viewModelModule = module { viewModel { ChallengeMainViewModel(get(), get(), get(), get(), get()) } - viewModel { ChallengeDetailViewModel(get(), get()) } + viewModel { ChallengeDetailViewModel(get(), get(), get()) } viewModel { ChallengeExitViewModel(get(), get()) } viewModel { UserPageViewModel( diff --git a/data/src/main/java/com/whyranoid/data/datasource/challenge/ChallengeService.kt b/data/src/main/java/com/whyranoid/data/datasource/challenge/ChallengeService.kt index 75e7b06d..0b089849 100644 --- a/data/src/main/java/com/whyranoid/data/datasource/challenge/ChallengeService.kt +++ b/data/src/main/java/com/whyranoid/data/datasource/challenge/ChallengeService.kt @@ -4,7 +4,7 @@ import com.whyranoid.data.API import com.whyranoid.data.model.StatusWithMessage import com.whyranoid.data.model.challenge.BadgeResponse import com.whyranoid.data.model.challenge.ChallengeDetailResponse -import com.whyranoid.data.model.challenge.ChallengePreviewResponse +import com.whyranoid.data.model.challenge.ChallengeResponse import com.whyranoid.data.model.challenge.request.ChallengeChangeStatusRequest import com.whyranoid.data.model.challenge.request.ChallengeStartRequest import retrofit2.Response @@ -18,19 +18,19 @@ interface ChallengeService { @GET(API.NEW_CHALLENGE) suspend fun getNewChallenges( @Query("walkieId") uid: Int, - ): Response> + ): Response> @GET(API.PROGRESSING_CHALLENGE) - suspend fun getMyProcessingChallenges(@Query("walkieId") uid: Int): Response> + suspend fun getMyProcessingChallenges(@Query("walkieId") uid: Int): Response> @GET(API.TOP_RANK_CHALLENGE) - suspend fun getTopRankChallenges(): Response> + suspend fun getTopRankChallenges(): Response> @GET(API.CHALLENGE_CATEGORY) suspend fun getChallengePreviewsByType( @Query("walkieId") uid: Int, @Query("category") type: String - ): Response> + ): Response> @GET(API.CHALLENGE_DETAIL) suspend fun getChallengeDetail( diff --git a/data/src/main/java/com/whyranoid/data/model/challenge/BadgeResponse.kt b/data/src/main/java/com/whyranoid/data/model/challenge/BadgeResponse.kt index 7a8e1e47..3af750e1 100644 --- a/data/src/main/java/com/whyranoid/data/model/challenge/BadgeResponse.kt +++ b/data/src/main/java/com/whyranoid/data/model/challenge/BadgeResponse.kt @@ -3,13 +3,15 @@ package com.whyranoid.data.model.challenge data class BadgeResponse( val badgeId: Int, val badgeName: String, + val failureImg: String? = null, val img: String ) { fun toBadge(): com.whyranoid.domain.model.challenge.Badge { return com.whyranoid.domain.model.challenge.Badge( id = badgeId.toLong(), name = badgeName, - imageUrl = img + imageUrl = img, + failureImageUrl = failureImg ) } } \ No newline at end of file diff --git a/data/src/main/java/com/whyranoid/data/model/challenge/ChallengeDetailResponse.kt b/data/src/main/java/com/whyranoid/data/model/challenge/ChallengeDetailResponse.kt index 877bcfa7..b56a44a7 100644 --- a/data/src/main/java/com/whyranoid/data/model/challenge/ChallengeDetailResponse.kt +++ b/data/src/main/java/com/whyranoid/data/model/challenge/ChallengeDetailResponse.kt @@ -1,29 +1,38 @@ package com.whyranoid.data.model.challenge +import com.whyranoid.domain.model.challenge.Challenge import com.whyranoid.domain.model.challenge.ChallengeType +import com.whyranoid.domain.util.EMPTY data class ChallengeDetailResponse( val challenge: ChallengeFromServer, val walkies: List ) { - fun toChallenge(): com.whyranoid.domain.model.challenge.Challenge { - val participants = walkies.map { it.toUser() } - return com.whyranoid.domain.model.challenge.Challenge( - badge = challenge.badge.toBadge(), - calorie = challenge.calorie, - challengeType = ChallengeType.getChallengeTypeByString(challenge.category), - id = challenge.challengeId.toLong(), - contents = challenge.content, - imageUrl = challenge.img, - title = challenge.name, - process = challenge.progress, - participants = participants, - participantCount = participants.size, - period = challenge.period, - distance = challenge.distance, - endTime = challenge.endTime, - startTime = challenge.startTime, - status = challenge.status + fun toChallenge(): Challenge { + val challenge = Challenge( + challenge.accCalories, + challenge.accCount, + challenge.accDistance, + challenge.accTime, + challenge.badge.toBadge(), + challenge.calorie, + ChallengeType.getChallengeTypeByString(challenge.category ?: String.EMPTY), + challenge.challengeEdate, + challenge.challengeId, + challenge.challengeSdate, + challenge.content, + challenge.distance, + challenge.endTime, + challenge.goalCount, + challenge.img, + challenge.name, + challenge.period, + challenge.progress, + challenge.startTime, + challenge.status, + challenge.timeLimit, + walkies.map { it.toUser() } ) + return challenge } } \ No newline at end of file diff --git a/data/src/main/java/com/whyranoid/data/model/challenge/ChallengeFromServer.kt b/data/src/main/java/com/whyranoid/data/model/challenge/ChallengeFromServer.kt index dcfd4fe7..7755f3ea 100644 --- a/data/src/main/java/com/whyranoid/data/model/challenge/ChallengeFromServer.kt +++ b/data/src/main/java/com/whyranoid/data/model/challenge/ChallengeFromServer.kt @@ -1,17 +1,25 @@ package com.whyranoid.data.model.challenge data class ChallengeFromServer( + val accCalories: Double?, + val accCount: Int?, + val accDistance: Double?, + val accTime: String? = null, val badge: BadgeResponse, - val calorie: Int?, - val category: String, + val calorie: Int, + val category: String?, + val challengeEdate: String? = null, val challengeId: Int, + val challengeSdate: String? = null, val content: String, val distance: Int?, - val endTime: String, + val endTime: String?, + val goalCount: Int?, val img: String, val name: String, val period: Int?, - val progress: Int, - val startTime: String, - val status: String + val progress: Int?, + val startTime: String?, + val status: String?, + val timeLimit: Int? ) \ No newline at end of file diff --git a/data/src/main/java/com/whyranoid/data/model/challenge/ChallengePreviewResponse.kt b/data/src/main/java/com/whyranoid/data/model/challenge/ChallengeResponse.kt similarity index 71% rename from data/src/main/java/com/whyranoid/data/model/challenge/ChallengePreviewResponse.kt rename to data/src/main/java/com/whyranoid/data/model/challenge/ChallengeResponse.kt index cc9a3bb9..5cb448dd 100644 --- a/data/src/main/java/com/whyranoid/data/model/challenge/ChallengePreviewResponse.kt +++ b/data/src/main/java/com/whyranoid/data/model/challenge/ChallengeResponse.kt @@ -3,13 +3,20 @@ package com.whyranoid.data.model.challenge import com.whyranoid.domain.model.challenge.ChallengePreview import com.whyranoid.domain.model.challenge.ChallengeType -data class ChallengePreviewResponse( +data class ChallengeResponse( + val calorie: Int, val category: String, val challengeId: Int, + val distance: Int, + val endTime: String, + val goalCount: Int, val name: String, val newFlag: Int, + val period: Int, val progress: Int, - val status: String + val startTime: String, + val status: String, + val timeLimit: Int ) { fun toChallengePreview(): ChallengePreview { return ChallengePreview( diff --git a/domain/src/main/java/com/whyranoid/domain/model/challenge/Badge.kt b/domain/src/main/java/com/whyranoid/domain/model/challenge/Badge.kt index e9cb189d..cad8b888 100644 --- a/domain/src/main/java/com/whyranoid/domain/model/challenge/Badge.kt +++ b/domain/src/main/java/com/whyranoid/domain/model/challenge/Badge.kt @@ -4,30 +4,6 @@ data class Badge( val id: Long, val name: String, val imageUrl: String, + val failureImageUrl: String? = null, ) { - companion object { - val DUMMY = Badge( - id = 0L, - name = "badgeName", - imageUrl = "https://picsum.photos/250/250", - ) - - val DUMMY_LIST = listOf( - Badge( - id = 0L, - name = "badgeName1", - imageUrl = "https://picsum.photos/250/250", - ), - Badge( - id = 1L, - name = "badgeName2", - imageUrl = "https://picsum.photos/250/250", - ), - Badge( - id = 2L, - name = "badgeName3", - imageUrl = "https://picsum.photos/250/250", - ), - ) - } } diff --git a/domain/src/main/java/com/whyranoid/domain/model/challenge/Challenge.kt b/domain/src/main/java/com/whyranoid/domain/model/challenge/Challenge.kt index b0eb667c..37654d2a 100644 --- a/domain/src/main/java/com/whyranoid/domain/model/challenge/Challenge.kt +++ b/domain/src/main/java/com/whyranoid/domain/model/challenge/Challenge.kt @@ -3,19 +3,26 @@ package com.whyranoid.domain.model.challenge import com.whyranoid.domain.model.user.User data class Challenge( - val id: Long, - val imageUrl: String, - val title: String, - val contents: String, - val period: Int? = null, - val challengeType: ChallengeType, + val accCalories: Double?, + val accCount: Int?, + val accDistance: Double?, + val accTime: String?, val badge: Badge, - val participantCount: Int, - val participants: List, - val process: Int? = null, - val calorie: Int? = null, - val distance: Int? = null, - val endTime: String, - val startTime: String, - val status: String? = null + val calorie: Int, + val challengeType: ChallengeType, + val challengeEdate: String?, + val id: Int, + val challengeSdate: String?, + val content: String, + val distance: Int?, + val endTime: String?, + val goalCount: Int?, + val imageUrl: String, + val name: String, + val period: Int?, + val progress: Int?, + val startTime: String?, + val status: String?, + val timeLimit: Int?, + val walkies: List ) diff --git a/domain/src/main/java/com/whyranoid/domain/usecase/CompleteChallengeUseCase.kt b/domain/src/main/java/com/whyranoid/domain/usecase/CompleteChallengeUseCase.kt new file mode 100644 index 00000000..c07f5b51 --- /dev/null +++ b/domain/src/main/java/com/whyranoid/domain/usecase/CompleteChallengeUseCase.kt @@ -0,0 +1,15 @@ +package com.whyranoid.domain.usecase + +import com.whyranoid.domain.repository.ChallengeRepository +import javax.inject.Inject + +class CompleteChallengeUseCase @Inject constructor( + private val challengeRepository: ChallengeRepository, + private val getMyUidUseCase: GetMyUidUseCase +) { + suspend operator fun invoke(challengeId: Int): Result { + val myId = getMyUidUseCase() + return challengeRepository.changeChallengeStatus(challengeId,"C", myId.getOrNull()?.toInt() ?: -1) + } + +} \ No newline at end of file diff --git a/presentation/src/main/java/com/whyranoid/presentation/component/ChallengeGoalContent.kt b/presentation/src/main/java/com/whyranoid/presentation/component/ChallengeGoalContent.kt index 0f2a3758..aa1baf1a 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/component/ChallengeGoalContent.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/component/ChallengeGoalContent.kt @@ -55,7 +55,9 @@ fun ChallengeGoalContent( val progressBarColor = challengeColor.progressBarColor - val progress = if (challenge.process == null) 0f else requireNotNull(challenge.process) / 100f + val progress = + if (challenge.progress == null) 0f else requireNotNull(challenge.progress) / 100f + Spacer(modifier = Modifier.height(30.dp)) @@ -114,7 +116,7 @@ fun ChallengeGoalContent( dstSize = bitMapIconSize, dstOffset = IntOffset( (barEnd - bitMapIconSize.width / 2).toInt(), - - bitMapIconSize.height / 2, + -bitMapIconSize.height / 2, ), colorFilter = ColorFilter.tint(progressBarColor) ) @@ -158,9 +160,11 @@ fun ChallengeGoalContent( modifier = Modifier.weight(1f), // TODO : limit에 적합한 값 필요(새로운 필드) // TODO : time 포멧 변경 - goal = "${challenge.startTime}~${challenge.endTime}시 사이", limit = challenge.period.toString() + goal = "${challenge.startTime}~${challenge.endTime}시 사이", + limit = challenge.period.toString() ) } + ChallengeType.CALORIE -> { ChallengeGoalItem( modifier = Modifier.weight(1f), @@ -168,6 +172,7 @@ fun ChallengeGoalContent( goal = "기간", limit = challenge.period.toString() ) } + ChallengeType.DISTANCE -> { ChallengeGoalItem( modifier = Modifier.weight(1f), @@ -186,7 +191,8 @@ fun ChallengeGoalContent( ChallengeGoalItem( modifier = Modifier.weight(1f), - goal = "칼로리", limit = if (challenge.challengeType == ChallengeType.CALORIE) "${challenge.calorie}kcal" else "0kcal" + goal = "칼로리", + limit = if (challenge.challengeType == ChallengeType.CALORIE) "${challenge.calorie}kcal" else "0kcal" ) } diff --git a/presentation/src/main/java/com/whyranoid/presentation/component/bottomsheet/ChallengeExitModalBottomSheetContainer.kt b/presentation/src/main/java/com/whyranoid/presentation/component/bottomsheet/ChallengeExitModalBottomSheetContainer.kt index 869e0b6b..caa6f732 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/component/bottomsheet/ChallengeExitModalBottomSheetContainer.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/component/bottomsheet/ChallengeExitModalBottomSheetContainer.kt @@ -88,7 +88,7 @@ fun ChallengeExitModalBottomSheetContainer( Spacer(modifier = Modifier.height(16.dp)) Text( - text = "진행률 ${challenge.process}%", + text = "진행률 ${challenge.progress}%", style = WalkieTypography.Body2.copy( color = SystemColor.Negative, fontWeight = FontWeight(700) diff --git a/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeDetailScreen.kt b/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeDetailScreen.kt index 128152d7..496993fc 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeDetailScreen.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeDetailScreen.kt @@ -1,6 +1,5 @@ package com.whyranoid.presentation.screens.challenge -import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box @@ -32,14 +31,12 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import coil.compose.AsyncImage -import com.whyranoid.presentation.R import com.whyranoid.presentation.component.ChallengeGoalContent import com.whyranoid.presentation.component.UserIcon import com.whyranoid.presentation.component.bottomsheet.ChallengeExitModalBottomSheetContainer @@ -82,9 +79,17 @@ fun ChallengeDetailScreen( ChallengeDetailSideEffect.StartChallengeFailure -> { SingleToast.show(context, "챌린지를 시작할 수 없습니다.") } + + ChallengeDetailSideEffect.ChangeChallengeStatusSuccess -> { + SingleToast.show(context, "챌린지를 성공적으로 완료하였습니다.") + navController.popBackStack() + } + + ChallengeDetailSideEffect.ChangeChallengeStatusFailure -> { + SingleToast.show(context, "챌린지 완료에 실패하였습니다.") + } } } - ChallengeDetailContent(state, isChallenging, onNegativeButtonClicked = { navController.navigate( @@ -93,7 +98,11 @@ fun ChallengeDetailScreen( }, onStartChallengeButtonClicked = { viewModel.startChallenge(it) - }) + }, + onChallengeCompleteButtonClicked = { + viewModel.changeChallengeStatus(it) + } + ) } @OptIn(ExperimentalMaterialApi::class) @@ -102,7 +111,8 @@ fun ChallengeDetailContent( state: ChallengeDetailState, isChallenging: Boolean, onNegativeButtonClicked: (Long) -> Unit = {}, - onStartChallengeButtonClicked: (Int) -> Unit = { } + onStartChallengeButtonClicked: (Int) -> Unit = { }, + onChallengeCompleteButtonClicked: (Int) -> Unit = { } ) { val coroutineScope = rememberCoroutineScope() @@ -119,7 +129,7 @@ fun ChallengeDetailContent( coroutineScope = coroutineScope, modalSheetState = modalSheetState, onNegativeButtonClicked = { - onNegativeButtonClicked(challenge.id) + onNegativeButtonClicked(challenge.id.toLong()) } ) { Scaffold() { paddingValues -> @@ -132,7 +142,7 @@ fun ChallengeDetailContent( .padding(paddingValues) .verticalScroll(scrollState), ) { - // TODO: Async Image + AsyncImage( model = challenge.imageUrl, contentDescription = null, @@ -147,7 +157,7 @@ fun ChallengeDetailContent( ) { Text( - text = challenge.title, + text = challenge.name, fontSize = 20.sp, fontWeight = FontWeight(700), ) @@ -169,7 +179,7 @@ fun ChallengeDetailContent( vertical = 12.dp ), color = Color(0xFF989898), - text = challenge.contents, + text = challenge.content, fontSize = 15.sp, fontWeight = FontWeight(500), ) @@ -230,7 +240,7 @@ fun ChallengeDetailContent( Spacer(modifier = Modifier.height(10.dp)) Text( - text = "${challenge.participantCount}명", + text = "${challenge.walkies.count()}명", fontSize = 12.sp, fontWeight = FontWeight(500), ) @@ -238,7 +248,7 @@ fun ChallengeDetailContent( Spacer(modifier = Modifier.height(10.dp)) LazyRow { - challenge.participants.forEach { participant -> + challenge.walkies.forEach { participant -> item { UserIcon(user = participant) Spacer(modifier = Modifier.width(8.dp)) @@ -264,6 +274,13 @@ fun ChallengeDetailContent( textDecoration = TextDecoration.Underline ) Spacer(modifier = Modifier.height(40.dp)) + + + // Todo: remove + WalkiePositiveButton(text = "완료하기") { + onChallengeCompleteButtonClicked(challenge.id) + } + } } else { Spacer(modifier = Modifier.height(28.dp)) diff --git a/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeExitScreen.kt b/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeExitScreen.kt index f9d77257..44f8962b 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeExitScreen.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeExitScreen.kt @@ -119,13 +119,13 @@ fun ChallengeExitContent( Spacer(modifier = Modifier.height(17.dp)) Text( - text = challenge.title, + text = challenge.name, style = WalkieTypography.Title.copy(Color(0xFF929292)) ) Spacer(modifier = Modifier.height(5.dp)) Text( - text = "진행률 ${challenge.process}%", + text = "진행률 ${challenge.progress}%", style = WalkieTypography.Title.copy( color = Color(0xFF929292), fontSize = 15.sp @@ -137,7 +137,6 @@ fun ChallengeExitContent( horizontalArrangement = Arrangement.spacedBy(10.dp) ) { - val context = LocalContext.current Box( Modifier.weight(1f) ) { diff --git a/presentation/src/main/java/com/whyranoid/presentation/viewmodel/challenge/ChallengeDetailViewModel.kt b/presentation/src/main/java/com/whyranoid/presentation/viewmodel/challenge/ChallengeDetailViewModel.kt index e6a3ea68..81d6a7d8 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/viewmodel/challenge/ChallengeDetailViewModel.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/viewmodel/challenge/ChallengeDetailViewModel.kt @@ -2,6 +2,8 @@ package com.whyranoid.presentation.viewmodel.challenge import androidx.lifecycle.ViewModel import com.whyranoid.domain.model.challenge.Challenge +import com.whyranoid.domain.usecase.ChangeChallengeStatusUseCase +import com.whyranoid.domain.usecase.CompleteChallengeUseCase import com.whyranoid.domain.usecase.GetChallengeDetailUseCase import com.whyranoid.domain.usecase.StartChallengeUseCase import com.whyranoid.presentation.model.UiState @@ -15,6 +17,9 @@ sealed class ChallengeDetailSideEffect { object StartChallengeSuccess : ChallengeDetailSideEffect() object StartChallengeFailure : ChallengeDetailSideEffect() + + object ChangeChallengeStatusSuccess : ChallengeDetailSideEffect() + object ChangeChallengeStatusFailure : ChallengeDetailSideEffect() } data class ChallengeDetailState( @@ -23,7 +28,8 @@ data class ChallengeDetailState( class ChallengeDetailViewModel( private val getChallengeDetailUseCase: GetChallengeDetailUseCase, - private val startChallengeUseCase: StartChallengeUseCase + private val startChallengeUseCase: StartChallengeUseCase, + private val changeChallengeStatusUseCase: CompleteChallengeUseCase ) : ViewModel(), ContainerHost { override val container = @@ -49,5 +55,13 @@ class ChallengeDetailViewModel( } } + + fun changeChallengeStatus(challengeId: Int) = intent { + changeChallengeStatusUseCase(challengeId).onSuccess { + postSideEffect(ChallengeDetailSideEffect.ChangeChallengeStatusSuccess) + }.onFailure { + postSideEffect(ChallengeDetailSideEffect.ChangeChallengeStatusFailure) + } + } }