diff --git a/app/src/main/java/com/whyranoid/walkie/KoinModules.kt b/app/src/main/java/com/whyranoid/walkie/KoinModules.kt index 4b9fdaf7..628fa4ea 100644 --- a/app/src/main/java/com/whyranoid/walkie/KoinModules.kt +++ b/app/src/main/java/com/whyranoid/walkie/KoinModules.kt @@ -50,6 +50,7 @@ import com.whyranoid.domain.repository.RunningHistoryRepository import com.whyranoid.domain.repository.RunningRepository import com.whyranoid.domain.repository.UserRepository import com.whyranoid.domain.usecase.ChangeChallengeStatusUseCase +import com.whyranoid.domain.usecase.CompleteChallengeUseCase import com.whyranoid.domain.usecase.GetChallengeDetailUseCase import com.whyranoid.domain.usecase.GetChallengePreviewsByTypeUseCase import com.whyranoid.domain.usecase.GetChallengingPreviewsUseCase @@ -182,6 +183,7 @@ val useCaseModule = single { GetNewChallengePreviewsUseCase(get()) } single { GetChallengingPreviewsUseCase(get()) } single { GetChallengeDetailUseCase(get(), get()) } + single { CompleteChallengeUseCase(get(), get())} single { GetChallengePreviewsByTypeUseCase(get(), get()) } single { GetTopRankChallengePreviewsUseCase(get()) } single { StartChallengeUseCase(get(), get()) } 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 b56a44a7..280313e5 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 @@ -31,6 +31,7 @@ data class ChallengeDetailResponse( challenge.startTime, challenge.status, challenge.timeLimit, + challenge.time, walkies.map { it.toUser() } ) return challenge 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 7755f3ea..617e60d6 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 @@ -21,5 +21,6 @@ data class ChallengeFromServer( val progress: Int?, val startTime: String?, val status: String?, - val timeLimit: Int? + val timeLimit: Int?, + val time: Int?, ) \ No newline at end of file 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 37654d2a..dfb9e46d 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 @@ -24,5 +24,6 @@ data class Challenge( val startTime: String?, val status: String?, val timeLimit: Int?, - val walkies: List + val time: Int?, + val walkies: List, ) diff --git a/domain/src/main/java/com/whyranoid/domain/util/Extensions.kt b/domain/src/main/java/com/whyranoid/domain/util/Extensions.kt index b3d59ffd..9ba25a33 100644 --- a/domain/src/main/java/com/whyranoid/domain/util/Extensions.kt +++ b/domain/src/main/java/com/whyranoid/domain/util/Extensions.kt @@ -1,6 +1,9 @@ package com.whyranoid.domain.util import java.text.SimpleDateFormat +import java.time.Duration +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter val String.Companion.EMPTY: String get() = "" @@ -9,3 +12,25 @@ val String.Companion.BLANK: String get() = " " val String.Companion.DATE_FORMAT: String get() = "yyyy-MM-dd HH:mm:ss" val dateFormatter = SimpleDateFormat(String.DATE_FORMAT) + +fun getToday(): String { + val today = LocalDateTime.now() + val formatter = DateTimeFormatter.ofPattern(String.DATE_FORMAT) + return today.format(formatter) +} + +fun getDurationDifference(startDate: String, endDate: String): List { + + val formatter = DateTimeFormatter.ofPattern(String.DATE_FORMAT) + + val startDateTime = LocalDateTime.parse(startDate, formatter) + val endDateTime = LocalDateTime.parse(endDate, formatter) + + val duration = Duration.between(endDateTime, startDateTime) + val days = duration.toDays() + val hours = duration.toHours() % 24 + val minutes = duration.toMinutes() % 60 + + return listOf(days, hours, minutes) +} + diff --git a/presentation/src/main/java/com/whyranoid/presentation/component/ChallengeGoalContent.kt b/presentation/src/main/java/com/whyranoid/presentation/component/challenge/ChallengeGoalContent.kt similarity index 98% rename from presentation/src/main/java/com/whyranoid/presentation/component/ChallengeGoalContent.kt rename to presentation/src/main/java/com/whyranoid/presentation/component/challenge/ChallengeGoalContent.kt index aa1baf1a..363e35e9 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/component/ChallengeGoalContent.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/component/challenge/ChallengeGoalContent.kt @@ -1,5 +1,4 @@ package com.whyranoid.presentation.component - import android.graphics.Bitmap import android.graphics.BitmapFactory import androidx.compose.foundation.Canvas @@ -37,6 +36,7 @@ import androidx.compose.ui.unit.sp import com.whyranoid.domain.model.challenge.Challenge import com.whyranoid.domain.model.challenge.ChallengeType import com.whyranoid.presentation.R +import com.whyranoid.presentation.component.challenge.ChallengeGoalItem import com.whyranoid.presentation.theme.ChallengeColor.getColor @Composable diff --git a/presentation/src/main/java/com/whyranoid/presentation/component/challenge/ChallengeGoalIndicator.kt b/presentation/src/main/java/com/whyranoid/presentation/component/challenge/ChallengeGoalIndicator.kt new file mode 100644 index 00000000..f2e5df22 --- /dev/null +++ b/presentation/src/main/java/com/whyranoid/presentation/component/challenge/ChallengeGoalIndicator.kt @@ -0,0 +1,230 @@ +package com.whyranoid.presentation.component.challenge + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.progressSemantics +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Divider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.drawText +import androidx.compose.ui.text.rememberTextMeasurer +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.whyranoid.domain.model.challenge.Challenge +import com.whyranoid.domain.model.challenge.ChallengeType +import com.whyranoid.domain.util.EMPTY +import com.whyranoid.domain.util.getDurationDifference +import com.whyranoid.domain.util.getToday +import com.whyranoid.presentation.theme.ChallengeColor.getColor +import com.whyranoid.presentation.theme.WalkieColor +import com.whyranoid.presentation.theme.WalkieTypography +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + +@Composable +fun ChallengeGoalIndicator(challenge: Challenge) { + + val challengeColor = challenge.challengeType.getColor() + + Column( + modifier = Modifier + .fillMaxWidth() + .clip(shape = RoundedCornerShape(15.dp)) + .background(challengeColor.backgroundColor), + horizontalAlignment = Alignment.CenterHorizontally + ) { + + val progressBarColor = challengeColor.progressBarColor + + val progress = + if (challenge.progress == null) 0f else requireNotNull(challenge.progress) / 100f + + val textMeasure = rememberTextMeasurer() + + Spacer(modifier = Modifier.height(20.dp)) + + Row( + modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically + ) { + + ChallengeGoalItem( + modifier = Modifier.weight(1f), + goal = "목표", + limit = when (challenge.challengeType) { + ChallengeType.LIFE -> { + "연속 " + challenge.period.toString() + "일" + } + + ChallengeType.CALORIE -> { + challenge.calorie.toString() + "kcal" + } + + ChallengeType.DISTANCE -> { + "${challenge.distance}km" + } + }, + ) + + Divider( + color = challengeColor.progressBarColor, + modifier = Modifier + .height(55.dp) + .width(1.dp) + ) + + when (challenge.challengeType) { + ChallengeType.LIFE -> { + if (challenge.startTime != String.EMPTY) { + ChallengeGoalItem( + modifier = Modifier.weight(1f), + goal = "운동 시작 시간", + limit = "${challenge.startTime?.take(2)}-${challenge.endTime?.take(2)}시" + ) + } else { + ChallengeGoalItem( + modifier = Modifier.weight(1f), + goal = "일일 운동 시간", + limit = "${(challenge.time ?: 0) / 60} 시간" + ) + } + + } + + ChallengeType.CALORIE, ChallengeType.DISTANCE -> { + ChallengeGoalItem( + modifier = Modifier.weight(1f), + goal = "기간", + limit = challenge.timeLimit.toString() + "일 동안" + ) + } + } + + } + + Spacer(modifier = Modifier.height(20.dp)) + + if (challenge.status == "P") { + + Row( + Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + + when (challenge.challengeType) { + ChallengeType.LIFE -> { + Text( + "${challenge.accCount}일째 성공했어요!", + style = WalkieTypography.Body1_ExtraBold.copy( + color = challengeColor.progressBarColor + ) + ) + } + + ChallengeType.CALORIE -> { + Text( + "${challenge.calorie?.minus(challenge.accCalories?.toInt() ?: 0)}kcal 만큼 남았어요", + style = WalkieTypography.Body1_ExtraBold.copy( + color = challengeColor.progressBarColor + ) + ) + } + + ChallengeType.DISTANCE -> { + Text( + "${challenge.distance?.minus(challenge.accDistance?.toInt() ?: 0)}m 만큼 남았어요", + style = WalkieTypography.Body1_ExtraBold.copy( + color = challengeColor.progressBarColor + ) + ) + } + } + + + Text( + "${challenge.progress}%", + style = WalkieTypography.Body1.copy( + color = challengeColor.progressBarColor + ) + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + + Canvas( + modifier = Modifier + .progressSemantics(progress) + .fillMaxWidth() + .padding(horizontal = 20.dp) + .height(4.dp) + ) { + + val width = size.width + val height = size.height + + val yOffset = height / 2 + + val isLtr = layoutDirection == LayoutDirection.Ltr + val barStart = (if (isLtr) 0f else 1f - progress) * width + val barEnd = (if (isLtr) progress else 1f) * width + + // draw Background + drawLine( + Color.White, + Offset(0f, yOffset), + Offset(1f * width, yOffset), + 2.dp.toPx(), + cap = StrokeCap.Round, + ) + + // Progress line + drawLine( + progressBarColor, + Offset(barStart, yOffset), + Offset(barEnd, yOffset), + 4.dp.toPx(), + cap = StrokeCap.Round, + ) + + } + + Spacer(modifier = Modifier.height(10.dp)) + + val deadLineInfo = getDurationDifference( + getToday(), challenge.challengeEdate ?: getToday() + ) + + Text( + text = "${deadLineInfo[0]}일 ${deadLineInfo[1]}시간 ${deadLineInfo[2]}분" + "남음", + style = WalkieTypography.Body2.copy( + color = Color(0xFF666666) + ) + ) + + Spacer(modifier = Modifier.height(10.dp)) + + } + } +} diff --git a/presentation/src/main/java/com/whyranoid/presentation/component/ChallengeGoalItem.kt b/presentation/src/main/java/com/whyranoid/presentation/component/challenge/ChallengeGoalItem.kt similarity index 63% rename from presentation/src/main/java/com/whyranoid/presentation/component/ChallengeGoalItem.kt rename to presentation/src/main/java/com/whyranoid/presentation/component/challenge/ChallengeGoalItem.kt index debbc979..a26f4f02 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/component/ChallengeGoalItem.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/component/challenge/ChallengeGoalItem.kt @@ -1,15 +1,14 @@ -package com.whyranoid.presentation.component +package com.whyranoid.presentation.component.challenge import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height -import androidx.compose.material.Text +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp +import com.whyranoid.presentation.theme.WalkieTypography @Composable fun ChallengeGoalItem( @@ -24,16 +23,14 @@ fun ChallengeGoalItem( ) { Text( text = goal, - fontSize = 15.sp, - fontWeight = FontWeight(500) + style = WalkieTypography.Body1, ) - Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(14.dp)) Text( text = limit, - fontSize = 20.sp, - fontWeight = FontWeight(700) + style = WalkieTypography.Title, ) } diff --git a/presentation/src/main/java/com/whyranoid/presentation/component/ChallengeItem.kt b/presentation/src/main/java/com/whyranoid/presentation/component/challenge/ChallengeItem.kt similarity index 97% rename from presentation/src/main/java/com/whyranoid/presentation/component/ChallengeItem.kt rename to presentation/src/main/java/com/whyranoid/presentation/component/challenge/ChallengeItem.kt index 441daa9a..3ee123a9 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/component/ChallengeItem.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/component/challenge/ChallengeItem.kt @@ -1,4 +1,4 @@ -package com.whyranoid.presentation.component +package com.whyranoid.presentation.component.challenge import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement diff --git a/presentation/src/main/java/com/whyranoid/presentation/component/ChallengingItem.kt b/presentation/src/main/java/com/whyranoid/presentation/component/challenge/ChallengingItem.kt similarity index 97% rename from presentation/src/main/java/com/whyranoid/presentation/component/ChallengingItem.kt rename to presentation/src/main/java/com/whyranoid/presentation/component/challenge/ChallengingItem.kt index a1087ddd..15c7dec3 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/component/ChallengingItem.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/component/challenge/ChallengingItem.kt @@ -1,4 +1,4 @@ -package com.whyranoid.presentation.component +package com.whyranoid.presentation.component.challenge import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box 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 496993fc..98644fce 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 @@ -37,7 +37,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import coil.compose.AsyncImage -import com.whyranoid.presentation.component.ChallengeGoalContent +import com.whyranoid.presentation.component.challenge.ChallengeGoalIndicator import com.whyranoid.presentation.component.UserIcon import com.whyranoid.presentation.component.bottomsheet.ChallengeExitModalBottomSheetContainer import com.whyranoid.presentation.component.button.WalkiePositiveButton @@ -142,7 +142,7 @@ fun ChallengeDetailContent( .padding(paddingValues) .verticalScroll(scrollState), ) { - + // TODO: Async Image AsyncImage( model = challenge.imageUrl, contentDescription = null, @@ -197,7 +197,7 @@ fun ChallengeDetailContent( Spacer(modifier = Modifier.height(10.dp)) - ChallengeGoalContent(challenge) + ChallengeGoalIndicator(challenge) Spacer(modifier = Modifier.height(42.dp)) diff --git a/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeMainScreen.kt b/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeMainScreen.kt index 4af84e4f..ba708cec 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeMainScreen.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/screens/challenge/ChallengeMainScreen.kt @@ -35,8 +35,8 @@ import androidx.navigation.NavController import com.whyranoid.domain.model.challenge.ChallengePreview import com.whyranoid.domain.model.challenge.ChallengeType import com.whyranoid.presentation.R -import com.whyranoid.presentation.component.ChallengeItem -import com.whyranoid.presentation.component.ChallengingItem +import com.whyranoid.presentation.component.challenge.ChallengeItem +import com.whyranoid.presentation.component.challenge.ChallengingItem import com.whyranoid.presentation.component.bar.WalkieTopBar import com.whyranoid.presentation.reusable.WalkieCircularProgressIndicator import com.whyranoid.presentation.theme.ChallengeColor.getColor diff --git a/presentation/src/main/java/com/whyranoid/presentation/screens/mypage/tabs/ChallengePage.kt b/presentation/src/main/java/com/whyranoid/presentation/screens/mypage/tabs/ChallengePage.kt index bb2f9be7..af526ff8 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/screens/mypage/tabs/ChallengePage.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/screens/mypage/tabs/ChallengePage.kt @@ -15,7 +15,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.whyranoid.domain.model.challenge.ChallengePreview -import com.whyranoid.presentation.component.ChallengeItem +import com.whyranoid.presentation.component.challenge.ChallengeItem import com.whyranoid.presentation.component.button.WalkiePositiveButton import com.whyranoid.presentation.theme.ChallengeColor.getColor import com.whyranoid.presentation.theme.WalkieTypography