From 3063446b511a0d21cd3f9f8d4ec3d3d164e6f890 Mon Sep 17 00:00:00 2001 From: Sadturtleman Date: Thu, 8 Oct 2026 20:48:42 +0900 Subject: [PATCH] =?UTF-8?q?feat(challenge):=20=EC=83=81=EC=84=B8=EC=9D=98?= =?UTF-8?q?=20=EC=9D=B8=EC=A6=9D=20=EB=B0=A9=EB=B2=95=20=EC=B9=B4=EB=93=9C?= =?UTF-8?q?=EC=97=90=20=EC=9D=B8=EC=A6=9D=20=EB=B0=A9=EB=B2=95=20=EC=95=88?= =?UTF-8?q?=EB=82=B4(guide)=EB=A5=BC=20=EB=8D=A7=EB=B6=99=EC=9D=B8?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 상세 응답 verification.guide 를 VerificationConfig.guide 로 옮긴다 - 참여 전 상세의 「인증 방법」, 방 정보 탭의 「인증 규칙」 카드에서 기존 설명 아래에 표시한다 - 서버가 아직 채우는 중(null)이면 「아직 입력중입니다.」를 보여준다 Co-Authored-By: Claude Opus 5.5 (1M context) --- .../data/dto/ChallengeCommonResponse.kt | 5 ++++- .../data/dto/ChallengeDetailMappingTest.kt | 13 ++++++++++++ .../domain/entity/RoutineVerification.kt | 2 ++ .../common/VerificationGuideText.kt | 20 +++++++++++++++++++ .../detail/component/DetailInfoPage.kt | 2 ++ .../detail/component/RoomInfoTab.kt | 2 ++ 6 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 challenge/presentation/src/main/kotlin/com/ruleup/challenge/presentation/common/VerificationGuideText.kt diff --git a/challenge/data/src/main/kotlin/com/ruleup/challenge/data/dto/ChallengeCommonResponse.kt b/challenge/data/src/main/kotlin/com/ruleup/challenge/data/dto/ChallengeCommonResponse.kt index 8019bf9d4..c12cc9acc 100644 --- a/challenge/data/src/main/kotlin/com/ruleup/challenge/data/dto/ChallengeCommonResponse.kt +++ b/challenge/data/src/main/kotlin/com/ruleup/challenge/data/dto/ChallengeCommonResponse.kt @@ -55,7 +55,7 @@ internal fun PenaltiesResponse?.toDomain(): ChallengePenalties = watcher = this?.watcher ?: false, ) -/** 인증 `{ type, method, detail, requiredPermissions }`. */ +/** 인증 `{ type, method, detail, requiredPermissions, guide }`. */ @Serializable data class VerificationResponse( @SerialName("type") @@ -67,6 +67,8 @@ data class VerificationResponse( val detail: String? = null, @SerialName("requiredPermissions") val requiredPermissions: List? = null, + @SerialName("guide") + val guide: String? = null, ) /** 미지의 method 는 [VerificationMethod.SELF_CHECK] 로 떨어뜨린다 */ @@ -79,6 +81,7 @@ internal fun VerificationResponse?.toDomain(): VerificationConfig { method = method, detail = this?.detail, requiredPermissions = this?.requiredPermissions.orEmpty(), + guide = this?.guide, ) } diff --git a/challenge/data/src/test/kotlin/com/ruleup/challenge/data/dto/ChallengeDetailMappingTest.kt b/challenge/data/src/test/kotlin/com/ruleup/challenge/data/dto/ChallengeDetailMappingTest.kt index eeb9e3501..84e6c0cd4 100644 --- a/challenge/data/src/test/kotlin/com/ruleup/challenge/data/dto/ChallengeDetailMappingTest.kt +++ b/challenge/data/src/test/kotlin/com/ruleup/challenge/data/dto/ChallengeDetailMappingTest.kt @@ -22,4 +22,17 @@ class ChallengeDetailMappingTest { assertNull(ChallengeDetailResponse(challengeId = "c1", weeklyCount = 0).toDomain().weeklyCount) assertNull(ChallengeDetailResponse(challengeId = "c1", weeklyCount = 8).toDomain().weeklyCount) } + + @Test + fun `인증 방법 안내를 그대로 옮긴다`() { + val response = + ChallengeDetailResponse(challengeId = "c1", verification = VerificationResponse(guide = "매일 10,000걸음 이상 걸으면 자동 인증됩니다.")) + assertEquals("매일 10,000걸음 이상 걸으면 자동 인증됩니다.", response.toDomain().verification.guide) + } + + @Test + fun `인증 방법 안내가 아직 없으면 비워 둔다`() { + // 서버가 채우는 중이다. 화면이 「아직 입력중입니다.」로 가른다. + assertNull(ChallengeDetailResponse(challengeId = "c1", verification = VerificationResponse()).toDomain().verification.guide) + } } diff --git a/challenge/domain/src/main/kotlin/com/ruleup/challenge/domain/entity/RoutineVerification.kt b/challenge/domain/src/main/kotlin/com/ruleup/challenge/domain/entity/RoutineVerification.kt index a8a837ea2..43205f80c 100644 --- a/challenge/domain/src/main/kotlin/com/ruleup/challenge/domain/entity/RoutineVerification.kt +++ b/challenge/domain/src/main/kotlin/com/ruleup/challenge/domain/entity/RoutineVerification.kt @@ -58,6 +58,8 @@ data class VerificationConfig( // 표시 문구(예: "기상 06:00 ±10분 내 10걸음"). val detail: String? = null, val requiredPermissions: List = emptyList(), + // 인증 방법 안내. 생성 직후·빈도·목표값·인증 방식 변경 직후에는 서버가 채우는 중이라 null 이다. + val guide: String? = null, ) /** 목표값 입력 종류 */ diff --git a/challenge/presentation/src/main/kotlin/com/ruleup/challenge/presentation/common/VerificationGuideText.kt b/challenge/presentation/src/main/kotlin/com/ruleup/challenge/presentation/common/VerificationGuideText.kt new file mode 100644 index 000000000..20f87b603 --- /dev/null +++ b/challenge/presentation/src/main/kotlin/com/ruleup/challenge/presentation/common/VerificationGuideText.kt @@ -0,0 +1,20 @@ +package com.ruleup.challenge.presentation.common + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.ruleup.designsystem.theme.RuleUpTheme + +/** 인증 방법 안내(서버가 준 verification.guide). 서버가 아직 채우는 중이면 그렇다고 알린다. */ +@Composable +internal fun VerificationGuideText( + guide: String?, + modifier: Modifier = Modifier, +) { + Text( + text = guide ?: "아직 입력중입니다.", + color = if (guide != null) RuleUpTheme.colors.textSlate else RuleUpTheme.colors.textMuted, + style = RuleUpTheme.typography.body, + modifier = modifier, + ) +} diff --git a/challenge/presentation/src/main/kotlin/com/ruleup/challenge/presentation/detail/component/DetailInfoPage.kt b/challenge/presentation/src/main/kotlin/com/ruleup/challenge/presentation/detail/component/DetailInfoPage.kt index a0941ad60..18752afbf 100644 --- a/challenge/presentation/src/main/kotlin/com/ruleup/challenge/presentation/detail/component/DetailInfoPage.kt +++ b/challenge/presentation/src/main/kotlin/com/ruleup/challenge/presentation/detail/component/DetailInfoPage.kt @@ -28,6 +28,7 @@ import com.ruleup.challenge.domain.entity.ChallengeDetail import com.ruleup.challenge.domain.entity.ChallengeLimits import com.ruleup.challenge.domain.entity.JoinNote import com.ruleup.challenge.presentation.common.RequiredPermissionList +import com.ruleup.challenge.presentation.common.VerificationGuideText import com.ruleup.challenge.presentation.common.capacityLabel import com.ruleup.challenge.presentation.create.label import com.ruleup.designsystem.component.RuleUpCard @@ -108,6 +109,7 @@ internal fun DetailInfoPage( detail.verification.detail?.let { Text(text = it, color = RuleUpTheme.colors.textSlate, style = RuleUpTheme.typography.body) } + VerificationGuideText(guide = detail.verification.guide) // 가입 전에 어떤 권한을 내줘야 하는지 알게 한다 RequiredPermissionList(tokens = requiredPermissions, snapshot = permissions) } diff --git a/challenge/presentation/src/main/kotlin/com/ruleup/challenge/presentation/detail/component/RoomInfoTab.kt b/challenge/presentation/src/main/kotlin/com/ruleup/challenge/presentation/detail/component/RoomInfoTab.kt index a9e003fda..7a9e247f5 100644 --- a/challenge/presentation/src/main/kotlin/com/ruleup/challenge/presentation/detail/component/RoomInfoTab.kt +++ b/challenge/presentation/src/main/kotlin/com/ruleup/challenge/presentation/detail/component/RoomInfoTab.kt @@ -33,6 +33,7 @@ import com.ruleup.challenge.domain.entity.ChallengeRoom import com.ruleup.challenge.domain.entity.OwnerType import com.ruleup.challenge.domain.entity.TodayVerificationStatus import com.ruleup.challenge.presentation.common.RequiredPermissionList +import com.ruleup.challenge.presentation.common.VerificationGuideText import com.ruleup.challenge.presentation.common.capacityLabel import com.ruleup.designsystem.component.RuleUpCard import com.ruleup.designsystem.component.StatusChip @@ -423,6 +424,7 @@ internal fun VerificationRuleCard( color = RuleUpTheme.colors.textSlate, style = RuleUpTheme.typography.body, ) + VerificationGuideText(guide = detail.verification.guide) RequiredPermissionList(tokens = requiredPermissions, snapshot = permissions) } }