Skip to content

[API] 기록장 기록, 투표 수정#134

Merged
Nico1eKim merged 11 commits intoTHIP-TextHip:developfrom
Nico1eKim:api/#133-group_note_edit
Sep 11, 2025
Merged

[API] 기록장 기록, 투표 수정#134
Nico1eKim merged 11 commits intoTHIP-TextHip:developfrom
Nico1eKim:api/#133-group_note_edit

Conversation

@Nico1eKim
Copy link
Member

@Nico1eKim Nico1eKim commented Sep 10, 2025

➕ 이슈 링크


🔎 작업 내용

  • 기록 수정 api를 연결했습니다
  • 투표 수정 api를 연결했습니다

📸 스크린샷

투표 수정

1000032984.mp4

기록 수정

1000032985.mp4

Summary by CodeRabbit

  • 신규 기능
    • 그룹 기록/투표 수정 지원: 기존 내용·페이지·개요·제목·옵션을 사전 채움.
    • 네비게이션 인자 확장으로 수정 흐름 연결 강화.
  • UI
    • 투표 표시 개선: 퍼센트 대신 득표수 노출, 진행바는 비율로 애니메이션.
    • 입력 필드 개선: 읽기 전용/비활성 상태 지원, 총 페이지 표시 토글 추가.
    • 제목·의견 입력이 TextFieldValue로 전환되어 커서/포커스 보존 및 자동 포커스.
    • 작성/수정 모드에 따른 상단 타이틀(“기록 수정”/“투표 수정”) 적용.

@coderabbitai
Copy link

coderabbitai bot commented Sep 10, 2025

Walkthrough

방 기록/투표 수정 기능을 추가. PATCH 요청/응답 모델 생성, Retrofit Service/Repository 경로 추가. UI는 작성 화면에 편집 모드(prefill, 포커스, 비활성화 제어) 도입. 입력 컴포넌트(API) 다수 시그니처 변경(TextFieldValue·isEnabled·readOnly 등). 리스트 화면에서 “수정” 진입 및 라우팅/라우트 데이터 확장.

Changes

Cohort / File(s) Summary
Rooms PATCH 요청/응답 모델
app/.../data/model/rooms/request/RoomsPatchRecordRequest.kt, app/.../data/model/rooms/request/RoomsPatchVoteRequest.kt, app/.../data/model/rooms/response/RoomsPatchRecordResponse.kt, app/.../data/model/rooms/response/RoomsPatchVoteResponse.kt
기록/투표 수정용 직렬화 데이터 클래스 추가: Request(content:String), Response(roomId:Int).
Rooms API/Repository
app/.../data/service/RoomsService.kt, app/.../data/repository/RoomsRepository.kt
PATCH 엔드포인트 추가: rooms/{roomId}/records/{recordId}, rooms/{roomId}/votes/{voteId}. Repository에 patchRoomsRecord/patchRoomsVote 추가.
피드 응답/투표 표시
app/.../data/model/rooms/response/RoomsPostsResponse.kt, app/.../ui/common/buttons/GroupVoteButton.kt
VoteItems 필드 변경: percentage→count. UI는 퍼센트 계산을 count 기반으로 변경, 라벨을 “count표”로 변경.
공통 입력 컴포넌트
app/.../ui/common/forms/BookPageTextField.kt, app/.../ui/common/forms/BorderedTextField.kt
시그니처 확장: readOnly, showTotalPage, isEnabled 추가. suffix 노출 로직 및 focus 처리 조정.
그룹 노트 입력 컴포넌트
app/.../ui/group/note/component/OpinionInputSection.kt, app/.../ui/group/note/component/PageInputSection.kt, app/.../ui/group/note/component/VoteInputSection.kt
TextFieldValue 도입(문자열→TextFieldValue), focusRequester 추가, isEnabled 추가. 배치/게이팅 로직 일부 변경.
생성/편집 화면(노트)
app/.../ui/group/note/screen/GroupNoteCreateScreen.kt, app/.../ui/group/note/viewmodel/GroupNoteCreateViewModel.kt
편집 모드 도입(prefill: postId/page/content/isOverview). 의견 입력을 TextFieldValue로 전환. create/update 분기 및 patch 호출 추가.
생성/편집 화면(투표)
app/.../ui/group/note/screen/GroupVoteCreateScreen.kt, app/.../ui/group/note/viewmodel/GroupVoteCreateViewModel.kt
편집 모드 도입(prefill: postId/page/isOverview/title/options). 제목을 TextFieldValue로 전환. create/update 분기 및 patch 호출 추가.
피드 화면(리스트)
app/.../ui/group/note/screen/GroupNoteScreen.kt
수정 액션 콜백(onEditNoteClick/onEditVoteClick) 추가, 작성자 메뉴에 “수정” 항목 추가. 패딩 소폭 조정.
네비게이션 확장
app/.../ui/navigator/extensions/GroupNavigationExtensions.kt, app/.../ui/navigator/navigations/GroupNavigation.kt, app/.../ui/navigator/routes/GroupRoutes.kt
NoteCreate/VoteCreate 라우트에 편집 컨텍스트(postId/page/content/isOverview/title/options) 필드 추가 및 전달 로직 연결.
뷰모델 리팩토링(피드)
app/.../ui/group/note/viewmodel/GroupNoteViewModel.kt
초기화 로직을 refreshAllData()로 분리(동시 로딩 유지).
문자열 리소스
app/src/main/res/values/strings.xml
edit_record, edit_vote 문자열 추가.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant L as GroupNoteScreen
  participant N as GroupNoteCreateScreen
  participant VM as GroupNoteCreateViewModel
  participant R as RoomsRepository
  participant S as RoomsService
  participant API as Server

  U->>L: 게시물에서 "수정" 선택
  L->>N: 네비게이트(postId, page, content, isOverview, ...)
  N->>VM: initialize(..., postId, page, content, isOverview)
  VM->>VM: isEditMode = true, 필드 prefill
  U->>N: 내용 편집 후 저장 클릭
  N->>VM: CreateRecordClicked
  alt isEditMode
    VM->>R: patchRoomsRecord(roomId, recordId, content)
    R->>S: PATCH /rooms/{roomId}/records/{recordId}
    S->>API: Request
    API-->>S: BaseResponse(roomId)
    S-->>R: Response
    R-->>VM: Result
    VM-->>N: 성공 상태 업데이트
  else create
    VM->>R: createRoomsRecord(...)
  end
Loading
sequenceDiagram
  autonumber
  actor U as User
  participant L as GroupNoteScreen
  participant V as GroupVoteCreateScreen
  participant VM as GroupVoteCreateViewModel
  participant R as RoomsRepository
  participant S as RoomsService
  participant API as Server

  U->>L: 투표에서 "수정" 선택
  L->>V: 네비게이트(postId, page, isOverview, title, options)
  V->>VM: initialize(..., postId, page, isOverview, title, options)
  VM->>VM: isEditMode = true, 필드 prefill
  U->>V: 제목/옵션 확인 후 저장
  V->>VM: CreateVoteClicked
  alt isEditMode
    VM->>R: patchRoomsVote(roomId, voteId, content=titleValue.text)
    R->>S: PATCH /rooms/{roomId}/votes/{voteId}
    S->>API: Request
    API-->>S: BaseResponse(roomId)
    S-->>R: Response
    R-->>VM: Result
    VM-->>V: 성공 상태 업데이트
  else create
    VM->>R: createRoomsVote(...)
  end
Loading
sequenceDiagram
  autonumber
  participant Feed as GroupNoteScreen
  participant Nav as NavHostController
  participant Routes as GroupRoutes
  Feed->>Nav: navigateToGroupNoteCreate(..., postId,page,content,isOverview)
  Nav->>Routes: GroupRoutes.NoteCreate(..., postId,...)
  Routes-->>Nav: Dest args 확장
  Nav-->>Feed: 화면 전환
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

Pre-merge checks (2 passed, 3 warnings)

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning 이 PR에는 기록/투표 수정 API 연결 범위를 넘어서는 범용 UI 컴포넌트(BookPageTextField, BorderedTextField, GroupVoteButton) 변경 및 데이터 모델 필드명 변경(RoomsPostsResponse의 percentage→count)이 포함되어 있어 이슈 #133의 목적과 직접 연관성이 떨어집니다. API 연결과 직접 관계없는 컴포넌트 리팩터링과 모델 필드 변경은 별도 PR로 분리하거나 변경 범위를 이슈 목표에 맞게 축소해주세요.
Description Check ⚠️ Warning 현재 PR 설명은 이슈 링크, 작업 내용, 스크린샷 섹션만 포함되어 있으며 템플릿에 요구되는 ‘해결하지 못한 과제’와 ‘리뷰어들에게’ 섹션이 누락되어 있습니다. 템플릿에 따라 해결하지 못한 과제 목록과 리뷰어에게 참고사항을 작성하는 섹션을 추가해주세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 7.89% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed 제목 “[API] 기록장 기록, 투표 수정”은 PR의 주요 변경사항인 기록 수정 API와 투표 수정 API 연결을 명확하게 요약하고 있어 간결하고 적절합니다.
Linked Issues Check ✅ Passed 연결된 이슈 #133의 상세 목표인 기록 수정 API와 투표 수정 API 연결 작업이 서비스, 리포지토리, 데이터 모델, UI 및 네비게이션 전반에 걸쳐 모두 구현되었습니다.

Poem

깡총, 깡총! 패치 길 열렸네 —
기록도 투표도 살짝 고쳐 보네. ✍️
포커스 톡, 값은 미리 채워두고,
토글은 살포시 휴식 모드로.
오늘도 코드밭에 당근 한 줌,
수정 완료! 뛰는 토끼의 줌! 🥕🐇

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupVoteCreateViewModel.kt (1)

115-119: 옵션 인덱스 범위 체크 누락으로 인한 크래시 가능성

UI 상 비정상 인덱스가 전달되면 IndexOutOfBoundsException이 발생할 수 있습니다. 방어 로직을 추가해 주세요.

-            is GroupVoteCreateEvent.OptionChanged -> _uiState.update {
-                val newOptions = it.options.toMutableList()
-                newOptions[event.index] = event.text
-                it.copy(options = newOptions)
-            }
+            is GroupVoteCreateEvent.OptionChanged -> _uiState.update {
+                val newOptions = it.options.toMutableList()
+                if (event.index in newOptions.indices) {
+                    newOptions[event.index] = event.text
+                }
+                it.copy(options = newOptions)
+            }

-            is GroupVoteCreateEvent.RemoveOptionClicked -> if (_uiState.value.options.size > 2) {
-                _uiState.update {
-                    val newOptions = it.options.toMutableList()
-                    newOptions.removeAt(event.index)
-                    it.copy(options = newOptions)
-                }
-            }
+            is GroupVoteCreateEvent.RemoveOptionClicked -> _uiState.update {
+                val newOptions = it.options.toMutableList()
+                if (event.index in newOptions.indices && newOptions.size > 2) {
+                    newOptions.removeAt(event.index)
+                }
+                it.copy(options = newOptions)
+            }

Also applies to: 133-139

🧹 Nitpick comments (19)
app/src/main/java/com/texthip/thip/ui/common/buttons/GroupVoteButton.kt (2)

50-59: 정밀도 손실로 애니메이션 계단현상 가능 — Float 비율로 직접 애니메이션 권장

정수 퍼센트(toInt())로 먼저 잘라내면 진행 바가 1% 단위로만 움직여 덜 매끄럽습니다. 비율(Float)을 그대로 타겟으로 쓰면 부드럽습니다.

-            val votePercent = if (totalVotes > 0) {
-                (item.count.toFloat() / totalVotes * 100).toInt()
-            } else {
-                0
-            }
-
-            val animatedPercent by animateFloatAsState(
-                targetValue = votePercent / 100f,
+            val progressFraction = if (totalVotes > 0) {
+                item.count.toFloat() / totalVotes
+            } else {
+                0f
+            }
+
+            val animatedPercent by animateFloatAsState(
+                targetValue = progressFraction,
                 animationSpec = tween(durationMillis = 500)
             )

설명: 변수명은 유지(animatedPercent)하여 하단 fillMaxWidth(animatedPercent)는 수정 없이 동작합니다.


112-116: 표시 문자열 하드코딩 지양 — 문자열 리소스로 국제화(i18n) 대비

"${item.count}표"는 한글 고정 문자열입니다. 리소스로 분리해 포맷을 관리하면 지역화와 테스트가 수월합니다. 필요하면 퍼센트 병행 표기도 선택 옵션으로 둬도 좋습니다.

-                        Text(
-                            text = "${item.count}표",
+                        Text(
+                            text = stringResource(R.string.vote_count_format, item.count),
                             color = textColor,
                             style = fontStyle
                         )

추가 사항:

  • 임포트: import androidx.compose.ui.res.stringResource
  • res/values/strings.xml:
<string name="vote_count_format">%1$d표</string>

(영문화 시 예: "%1$d votes")

app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsPatchRecordRequest.kt (1)

5-8: 빈 문자열 방지 가드 고려

서버가 공백·빈 문자열을 거부한다면 상위 레이어(Repository/UseCase)에서 isBlank() 체크로 빠르게 실패 처리하는 편이 안전합니다.

app/src/main/java/com/texthip/thip/ui/common/forms/BorderedTextField.kt (1)

50-59: 비활성화 시 클릭 가드 보강 및 접근성 텍스트 현지화 제안

  • 방어적으로 클릭 가능 여부도 isEnabled에 종속시키면 안전합니다.
  • contentDescription에 하드코딩된 영어 문자열 대신 string 리소스 사용 권장.

적용 예:

-                            modifier = Modifier
-                                .clickable(enabled = iconEnabled) {
+                            modifier = Modifier
+                                .clickable(enabled = iconEnabled && isEnabled) {

Also applies to: 107-119

app/src/main/java/com/texthip/thip/ui/common/forms/BookPageTextField.kt (1)

76-90: readOnly에서 텍스트 자동 초기화 방지 가드

현재 포커스 획득 시 초기화 로직이 enabled만 보고 동작합니다. 방어적으로 !readOnly도 함께 확인하는 것이 안전합니다.

-                .then(
-                    if (enabled) {
+                .then(
+                    if (enabled && !readOnly) {
                         Modifier.onFocusChanged { focusState ->
app/src/main/java/com/texthip/thip/ui/group/note/component/OpinionInputSection.kt (1)

36-37: FocusRequester 기본값 처리 방식 점검

기본값에서 remember { FocusRequester() } 사용은 가능하나, 외부에서 포커스를 강제하지 않는 케이스가 더 많다면 내부에서 생성(hoist 불필요)하거나, 호출자가 항상 주입하도록 일관성 정리 권장.

app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPatchVoteResponse.kt (1)

6-8: 후행 쉼표 제거 제안(스타일 일관성/빌드 호환성)

Kotlin 1.4+에선 허용되지만, 옆 파일(RecordResponse)과 스타일을 맞추고 구버전 호환성 이슈를 예방합시다.

 data class RoomsPatchVoteResponse(
-    val roomId: Int,
+    val roomId: Int
 )
app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsPatchVoteRequest.kt (1)

7-8: 트레일링 콤마 스타일 확인 필요

프로젝트의 Kotlin/ktlint 설정에 따라 트레일링 콤마 허용 여부가 다릅니다. 팀 컨벤션이 미허용이면 제거해 주세요.

다음과 같이 정리 가능:

 data class RoomsPatchVoteRequest(
-    val content: String,
+    val content: String
 )
app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt (1)

322-334: 콘텐츠 전송 전 최소 전처리 권장(트림/빈값 방지)

사소하지만 API 일관성에 도움 됩니다. 공백만 있는 경우 서버에서 400을 주지 않는다면 클라이언트에서 방지하는 편이 안전합니다.

다음과 같이 트림 적용 및 빈값 가드 추가를 고려해 주세요:

-            request = RoomsPatchRecordRequest(
-                content = content
-            )
+            request = RoomsPatchRecordRequest(
+                content = content.trim()
+            )

필요 시 호출부(뷰모델)에서 require(content.isNotBlank()) 체크도 함께 권장합니다.

app/src/main/java/com/texthip/thip/ui/group/note/component/PageInputSection.kt (1)

75-80: readOnly 처리 일관성 보완 제안

isGeneralReview일 때도 읽기 전용으로 표시되면 UX가 더 명확합니다. 현재는 enabled만 꺼지고 readOnly는 isEnabled에만 연동됩니다. 둘을 결합해 상태를 명확히 해주세요.

-                enabled = !isGeneralReview && isEnabled,
-                readOnly = !isEnabled,
+                enabled = !isGeneralReview && isEnabled,
+                readOnly = isGeneralReview || !isEnabled,
app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt (1)

102-107: NoteCreate 네비게이션 파라미터 확장 OK

편집 흐름(postId, page, content, isOverview)을 선택적으로 전달하는 구조가 자연스럽습니다. 다만 선택 파라미터가 늘어난 만큼 “편집 페이로드” 데이터 클래스로 묶는 것도 가독성/확장성에 유리합니다.

app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt (1)

46-46: 변수 명명 일관성 개선 필요

postIdToEdit는 기록과 투표 모두에서 사용되므로, 더 일반적인 이름인 editingPostId로 변경하는 것이 좋겠습니다.

-    private var postIdToEdit: Int? = null // 수정할 포스트 ID 저장
+    private var editingPostId: Int? = null // 수정 중인 포스트 ID
app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt (1)

94-99: 포커스 요청 타이밍 개선 필요

고정된 100ms 지연 대신 컴포지션이 완료된 후 포커스를 요청하는 것이 더 안정적입니다.

     LaunchedEffect(uiState.isEditMode) {
         if (uiState.isEditMode) {
-            delay(100)
-            focusRequester.requestFocus()
+            // 컴포지션이 완료된 후 포커스 요청
+            focusRequester.requestFocus()
         }
     }
app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupVoteCreateScreen.kt (2)

51-55: 투표 옵션 타입 안전성 개선 필요

options 파라미터가 List<String>? 타입인데, 빈 리스트와 null을 구분해서 처리해야 할 수 있습니다.

수정 모드에서 options가 null이거나 빈 리스트인 경우를 명시적으로 처리하는 것이 좋겠습니다.


94-99: 포커스 요청 로직 중복

GroupNoteCreateScreen과 동일한 포커스 로직이 중복되어 있습니다. 공통 컴포저블로 추출을 고려해보세요.

// 공통 유틸리티 파일에 추가
@Composable
fun RequestFocusOnEditMode(
    isEditMode: Boolean,
    focusRequester: FocusRequester
) {
    LaunchedEffect(isEditMode) {
        if (isEditMode) {
            focusRequester.requestFocus()
        }
    }
}
app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupVoteCreateViewModel.kt (3)

81-96: 수정 모드에서 총평(isOverview)일 때 pageText 처리 일관화 제안

총평 선택 시 토글 로직(라인 121~127)에서는 pageText를 비우는데, 초기화에서는 항상 page를 문자열로 넣고 있습니다. 초기·토글 동작을 맞추면 UX 혼선을 줄일 수 있습니다.

-                    pageText = page.toString(),
-                    isGeneralReview = isOverview,
+                    pageText = if (isOverview) "" else page.toString(),
+                    isGeneralReview = isOverview,
+                    savedPageText = if (isOverview) page.toString() else it.savedPageText,

178-179: 하드코딩된 에러 문자열의 리소스화/분리 제안

ViewModel 내 사용자 노출 문자열은 다국어/테스트 용이성을 위해 리소스(or StringProvider)로 분리하는 것이 좋습니다.


67-77: initialize 파라미터 수 과다

초기화 인자가 많아 가독성이 떨어집니다. 편집 페이로드를 데이터 클래스로 묶는 것을 권장합니다(예: VoteEditPayload).

app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteScreen.kt (1)

427-428: 매직 넘버(98.dp) 상수화

헤더/오버레이 패딩 값이 분산되면 유지보수 비용이 증가합니다. 파일 상단에 상수로 추출해 재사용을 권장합니다.

-                        .padding(top = 98.dp)
+                        .padding(top = HEADER_OVERLAY_TOP_PADDING)

추가 코드(파일 상단 임의 위치):

private val HEADER_OVERLAY_TOP_PADDING = 98.dp
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 47e1df3 and 5b0c1ae.

📒 Files selected for processing (23)
  • app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsPatchRecordRequest.kt (1 hunks)
  • app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsPatchVoteRequest.kt (1 hunks)
  • app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPatchRecordResponse.kt (1 hunks)
  • app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPatchVoteResponse.kt (1 hunks)
  • app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPostsResponse.kt (1 hunks)
  • app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt (2 hunks)
  • app/src/main/java/com/texthip/thip/data/service/RoomsService.kt (4 hunks)
  • app/src/main/java/com/texthip/thip/ui/common/buttons/GroupVoteButton.kt (2 hunks)
  • app/src/main/java/com/texthip/thip/ui/common/forms/BookPageTextField.kt (2 hunks)
  • app/src/main/java/com/texthip/thip/ui/common/forms/BorderedTextField.kt (3 hunks)
  • app/src/main/java/com/texthip/thip/ui/group/note/component/OpinionInputSection.kt (5 hunks)
  • app/src/main/java/com/texthip/thip/ui/group/note/component/PageInputSection.kt (2 hunks)
  • app/src/main/java/com/texthip/thip/ui/group/note/component/VoteInputSection.kt (5 hunks)
  • app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt (5 hunks)
  • app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteScreen.kt (6 hunks)
  • app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupVoteCreateScreen.kt (6 hunks)
  • app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt (9 hunks)
  • app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt (2 hunks)
  • app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupVoteCreateViewModel.kt (7 hunks)
  • app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt (2 hunks)
  • app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt (3 hunks)
  • app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt (1 hunks)
  • app/src/main/res/values/strings.xml (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupVoteCreateScreen.kt (2)
app/src/main/java/com/texthip/thip/ui/common/topappbar/InputTopAppBar.kt (1)
  • InputTopAppBar (25-69)
app/src/main/java/com/texthip/thip/ui/group/note/component/VoteInputSection.kt (1)
  • VoteInputSection (34-118)
app/src/main/java/com/texthip/thip/ui/group/note/component/PageInputSection.kt (1)
app/src/main/java/com/texthip/thip/ui/common/buttons/ToggleSwitchButton.kt (1)
  • ToggleSwitchButton (23-54)
app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt (3)
app/src/main/java/com/texthip/thip/ui/common/topappbar/InputTopAppBar.kt (1)
  • InputTopAppBar (25-69)
app/src/main/java/com/texthip/thip/ui/group/note/component/OpinionInputSection.kt (1)
  • OpinionInputSection (29-90)
app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt (1)
  • onEvent (84-111)
🔇 Additional comments (31)
app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPostsResponse.kt (1)

35-40: "percentage" 키 사용 흔적 미발견 – 호환성 매핑은 필요 시 선택적 적용
프로젝트 전체에 percentage 응답 키 사용이 검색되지 않아 현 시점에선 @SerialName/@JsonNames 매핑이 필수는 아닙니다. 백엔드가 과거 percentage 키를 더 이상 사용하지 않는지 확인한 뒤 적용 여부를 결정하세요.

app/src/main/java/com/texthip/thip/ui/common/buttons/GroupVoteButton.kt (1)

42-42: 총 투표수 계산 도입 LGTM

sumOf { it.count }로 단순·명확합니다. 데이터 크기가 매우 크지 않다면 성능/오버플로 이슈 우려도 낮습니다.

app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPatchRecordResponse.kt (1)

5-8: 응답 모델 초안 LGTM

단순/명확하고 직렬화 요건 충족합니다.

app/src/main/java/com/texthip/thip/ui/common/forms/BorderedTextField.kt (1)

41-45: isEnabled 도입 LGTM

컴포넌트 인터랙션을 상위에서 제어할 수 있게 되어 일관성 좋아졌습니다.

app/src/main/java/com/texthip/thip/ui/common/forms/BookPageTextField.kt (1)

49-55: API 확장 LGTM

readOnly/showTotalPage 추가로 다양한 편집 모드 대응이 쉬워졌습니다.

app/src/main/java/com/texthip/thip/ui/group/note/component/OpinionInputSection.kt (2)

32-37: TextFieldValue 전환 및 길이 제한 처리 LGTM

입력 상태/커서 정보 보존되고, 최대 길이 제약도 간결합니다.

Also applies to: 51-56


32-37: 이전 시그니처 호출부 없음 확인 전체 .kt 파일 검사 결과 OpinionInputSection(text: String, …) 또는 onTextChange: (String) -> Unit 형태의 구버전 호출부가 검색되지 않았습니다.

app/src/main/res/values/strings.xml (1)

44-46: 문구 추가 LGTM

편집 플로우의 라벨링이 명확합니다. 다국어 지원 시 해당 키로 확장만 하면 됩니다.

app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsPatchVoteRequest.kt (1)

5-8: 간단 PATCH 요청 모델 정의 적절

서버 스키마와 일치한다면 그대로 사용해도 무방합니다.

app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt (1)

12-13: 신규 요청 모델 import 추가 OK

app/src/main/java/com/texthip/thip/data/service/RoomsService.kt (5)

9-10: 신규 PATCH 요청 모델 import 적절


29-30: 신규 PATCH 응답 모델 import 적절


42-42: @patch 도입 정상


204-209: 기록 PATCH 엔드포인트 정의 적절

기존 records 네이밍과 일치해 보입니다.


211-216: 투표 PATCH 경로 네이밍(복수/단수) 불일치 가능성

기존 생성/삭제가 vote(단수)인 반면 본 메서드는 votes(복수)입니다. 백엔드 스펙과 불일치 시 404 발생합니다.

스펙이 단수라면 아래처럼 수정해 주세요:

-    @PATCH("rooms/{roomId}/votes/{voteId}")
+    @PATCH("rooms/{roomId}/vote/{voteId}")
     suspend fun patchRoomsVote(

위 스크립트로 레포 전체의 경로 일관성도 함께 확인해 주세요(Repository 코멘트 참조).

app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt (2)

51-56: 노트 편집을 위한 파라미터 확장 설계 적절

기존 호출부 유지(기본값 null)와 편집 모드 프리필을 동시에 만족합니다.


63-69: 네비게이션 페이로드 크기/직렬화 확인 권장(List 전달)

options: List<String>?를 라우트 인자로 직렬화해 전달하면 항목 수가 많을 때 바인더 한도를 건드릴 수 있습니다. 라이브러리(toRoute)에서 안전하게 Bundle/JSON 변환되는지와 예상 최대 크기를 확인해 주세요. 필요 시 옵션은 ViewModel 측에서 postId로 재조회하는 방식이 안전합니다.

app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt (4)

354-366: 편집 네비게이션 파라미터 주입 OK — 단, 상태 읽기 타이밍 점검

클릭 시점에 viewModel.uiState.value를 직접 읽는 패턴은 안전하나, 비동기 로딩 중이면 최신 값이 아닐 수 있습니다. 초기값 요구사항(최근/전체 페이지)이 엄격하면 수집된 상태(collectAsState())에서 파생해 사용하는지 확인해 주세요.


415-424: 노트 편집 라우팅 파라미터 전달 OK

선택 탭 복귀 로직(savedStateHandle)과 함께 정상 동작할 것으로 보입니다.


445-450: 투표 편집 라우팅 파라미터 전달 OK

title/options 전달로 프리필 요구사항 충족.


367-381: 어떤 서비스의 Rooms PATCH API 스펙을 참고해야 할지 알려주세요.
사내 API 문서 URL 또는 관련 스펙(요청/응답 예시 포함)을 공유해 주시면 투표 옵션(option) 수정 지원 여부와 스키마 키를 확인해 드리겠습니다.

app/src/main/java/com/texthip/thip/ui/group/note/component/PageInputSection.kt (1)

113-120: ToggleSwitchButton onToggleChange에서 ‘전체 구간’ 문자열 주입 제거

  • 토글 ON 시 onPageTextChange(if (checked) allRangeText else "") 호출을 제거하고, 오직 토글 해제 시에만 pageText 초기화 로직만 수행
  • 전송 로직에서 isGeneralReview == true일 때 page 필드를 null 또는 미포함 처리하는지 확인 필요
app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt (3)

139-145: LGTM: 전달 인자 매핑 적정

roomId/recentPage/totalPage/isOverviewPossible 및 편집 관련 파라미터가 올바르게 매핑되었습니다.


113-118: GroupRoutes.NoteCreate 시그니처 불일치
NoteCreate 데이터 클래스는 roomId, recentBookPage, totalBookPage 등을 받으며 postId/page/content 매개변수는 존재하지 않습니다. 해당 리뷰 코멘트는 적용 대상이 아닙니다.

Likely an incorrect or invalid review comment.


127-133: VoteCreate의 options 전달 방식 검증
VoteCreate는 @serializable 데이터 클래스이지만, 네비게이션이 문자열 경로 기반이라면 List? 인자를 JSON 인코딩 등으로 직렬화/역직렬화해 전달해야 합니다. 타입 세이프 네비게이션을 쓰는지, 혹은 NavType.StringArray 등 리스트 전용 NavType을 등록해두었는지 확인하세요.

app/src/main/java/com/texthip/thip/ui/group/note/component/VoteInputSection.kt (1)

53-58: 상호작용 제어 흐름은 일관되고 명확합니다

외곽 클릭 포커스 해제, 옵션 필드의 isEnabled 연동, 항목 추가 버튼의 노출 조건 모두 의도에 맞게 동작합니다.

Also applies to: 91-93, 96-116

app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt (1)

50-53: nullable 파라미터 추가 검증 불필요
GroupRoutes.NoteCreate 데이터 클래스에서 postId, page, content, isOverview에 모두 = null 기본값이 정의되어 있고, type-safe navigation 호출부(GroupNavigation.kt)에서 항상 명시적으로 전달되므로 부분적 null 상태가 발생하지 않습니다.

Likely an incorrect or invalid review comment.

app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupVoteCreateScreen.kt (1)

108-109: 문자열 리소스 정의 확인 완료
R.string.create_voteR.string.edit_voteapp/src/main/res/values/strings.xml에 정의되어 있어 별도 수정이 필요 없습니다.

app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteScreen.kt (3)

519-529: 작성자 메뉴에 “수정” 항목 추가 — 동작 적합

postType 분기 처리 및 시트 닫기 처리(selectedPostForMenu = null)까지 되어 있어 UX 일관성이 좋습니다.


672-675: 프리뷰 시그니처 반영 완료

새 콜백을 프리뷰에서도 전달해 컴파일 안정성을 확보했습니다.


81-83: GroupNoteScreen/Content 호출부 확인 완료
GroupNavigation.kt 및 GroupNoteScreen.kt 내 모든 호출부가 신규 콜백(onEditNoteClick, onEditVoteClick)에 대해 기본값(empty lambda)을 활용하므로 추가 수정 불필요합니다.

Comment on lines +336 to +348
suspend fun patchRoomsVote(
roomId: Int,
voteId: Int,
content: String,
) = runCatching {
roomsService.patchRoomsVote(
roomId = roomId,
voteId = voteId,
request = RoomsPatchVoteRequest(
content = content,
)
).handleBaseResponse().getOrThrow()
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

vote PATCH 경로/ID 매핑 재확인 필요

서비스는 "rooms/{roomId}/votes/{voteId}"(복수형) 경로를 사용합니다. 기존 POST/DELETE는 단수형 vote를 사용 중이라 404 위험이 있습니다. 또한 UI에서 전달하는 postId가 실제 voteId와 동일한지 확인이 필요합니다.

아래 스크립트로 현재 경로 일관성을 점검해 주세요:


🏁 Script executed:

#!/bin/bash
# vote vs votes 경로 사용 현황 확인
rg -nP 'rooms/\{roomId\}/votes?/\{voteId\}' -g '!**/build/**'

# voteId로 사용되는 식별자 확인(예: postId를 그대로 쓰는지)
rg -nP '\b(voteId|recordId|postId)\b' app/src/main/java -C2

Length of output: 55050


RoomsService POST/DELETE 경로 plural 통일 및 voteId 매핑 검증

  • app/src/main/java/com/texthip/thip/data/service/RoomsService.kt (라인 150, 163):
    @POST("rooms/{roomId}/vote/{voteId}") /
    @DELETE("rooms/{roomId}/vote/{voteId}")
    @POST("rooms/{roomId}/votes/{voteId}") /
    @DELETE("rooms/{roomId}/votes/{voteId}")로 변경
  • UI에서 사용하는 postId가 백엔드의 voteId와 일치하는 값인지 재확인 필요
🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt around
lines 336 to 348, ensure the vote endpoint usage and ID mapping align with the
service changes: update any RoomsService POST/DELETE path annotations from
"rooms/{roomId}/vote/{voteId}" to "rooms/{roomId}/votes/{voteId}", confirm that
the repository continues to call the updated service methods, and verify the UI
is passing the backend's voteId (not postId) into patchRoomsVote so the voteId
parameter maps correctly to the backend resource.

Comment on lines +43 to 45
onInfoPositionCaptured: (LayoutCoordinates) -> Unit,
isEnabled: Boolean = true
) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

API 시그니처 확장: isEnabled 추가는 합리적입니다.

기본값을 제공해 소스 호환성 유지됩니다. 해당 파라미터 전달 여부를 최근 호출부(특히 편집 모드 진입 시)에서 일관되게 적용됐는지 한 번만 점검하면 됩니다.

호출부 점검 스크립트:


🏁 Script executed:

#!/bin/bash
rg -nP --type=kotlin -C2 '\bPageInputSection\(' app/src | sed -E 's/^/HIT: /'

Length of output: 2596


API 시그니처 확장: isEnabled 파라미터 전달 여부 확인
현재 GroupVoteCreateScreen.kt (라인 122)와 GroupNoteCreateScreen.kt (라인 125)에서 PageInputSection이 isEnabled 인자 없이 호출되어 디폴트(true)를 사용 중입니다. 에디팅 모드 진입 시 비활성화가 필요하다면 isEnabled = false를 명시적으로 넘겨주세요.

🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/group/note/component/PageInputSection.kt
around lines 43-45 the function adds an isEnabled: Boolean = true parameter but
callers GroupVoteCreateScreen.kt (line 122) and GroupNoteCreateScreen.kt (line
125) currently rely on the default true; update those call sites to explicitly
pass isEnabled = false when entering editing/disabled modes (or pass the
appropriate boolean state variable) so the component is correctly disabled, and
ensure any other callers that should reflect non-editable state are updated
likewise.

Comment on lines +46 to 47
focusRequester: FocusRequester = remember { FocusRequester() },
) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

기본 파라미터에서 remember 사용은 컴파일 에러

기본값에 remember를 사용할 수 없습니다(Composable 호출 불가). Nullable 파라미터로 받고 내부에서 remember로 대체하세요. 또한 .focusRequester(...) 사용부를 대체 변수로 교체해야 합니다.

-    maxOptions: Int = 5,
-    focusRequester: FocusRequester = remember { FocusRequester() },
+    maxOptions: Int = 5,
+    focusRequester: FocusRequester? = null,

함수 본문 내 보완(추가 코드):

val resolvedFocusRequester = focusRequester ?: remember { FocusRequester() }

사용부 교체:

-                .focusRequester(focusRequester),
+                .focusRequester(resolvedFocusRequester),

Also applies to: 67-67

🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/group/note/component/VoteInputSection.kt
around lines 46-47 (and also line 67), the parameter currently uses remember as
a default which causes a compile error because remember cannot be called in
parameter defaults; change the parameter type to nullable (FocusRequester?) and
remove the remember default, then inside the function create a
resolvedFocusRequester with val resolvedFocusRequester = focusRequester ?:
remember { FocusRequester() } and replace all usages of .focusRequester(...) or
references to the original parameter with resolvedFocusRequester so the
composable uses the remembered instance when none is provided.

Comment on lines +62 to 68
value = titleValue,
onValueChange = { if (it.text.length <= maxOptionLength) onTitleChange(it) },
textStyle = typography.smalltitle_m500_s18_h24.copy(color = colors.White),
modifier = Modifier
.fillMaxWidth(),
.fillMaxWidth()
.focusRequester(focusRequester),
cursorBrush = SolidColor(colors.NeonGreen),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

isEnabled=false에서도 제목 입력이 가능함

BasicTextField에 enabled/readOnly가 전달되지 않아 비활성화 상태에서도 편집이 가능합니다. 아래처럼 막아주세요.

-        BasicTextField(
+        BasicTextField(
             value = titleValue,
             onValueChange = { if (it.text.length <= maxOptionLength) onTitleChange(it) },
+            enabled = isEnabled,
+            readOnly = !isEnabled,
             textStyle = typography.smalltitle_m500_s18_h24.copy(color = colors.White),
             modifier = Modifier
                 .fillMaxWidth()
-                .focusRequester(focusRequester),
+                .focusRequester(resolvedFocusRequester),

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/group/note/component/VoteInputSection.kt
around lines 62–68, the BasicTextField currently allows editing even when
isEnabled is false; pass the proper flags to disable input by adding enabled =
isEnabled and readOnly = !isEnabled to the text field call (or if BasicTextField
variant used here doesn't support enabled, switch to the TextField API or wrap
the field with a read-only/consuming pointer modifier) so that when isEnabled is
false the user cannot edit the title.

Comment on lines +68 to +81
if (postId != null && page != null && content != null && isOverview != null) {
this.postIdToEdit = postId
_uiState.update {
it.copy(
isEditMode = true,
pageText = page.toString(),
isGeneralReview = isOverview,
opinionTextFieldValue = TextFieldValue(
text = content,
selection = TextRange(content.length)
)
)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

수정 모드 진입 조건 개선 필요

현재 모든 파라미터가 null이 아닌 경우에만 수정 모드로 진입하는데, isOverviewfalse일 수 있으므로 null 체크가 불필요합니다. 또한 content가 빈 문자열인 경우도 유효한 케이스일 수 있습니다.

-        // postId가 null이 아니면 수정 모드로 진입
-        if (postId != null && page != null && content != null && isOverview != null) {
-            this.postIdToEdit = postId
+        // postId가 null이 아니면 수정 모드로 진입
+        if (postId != null && page != null && content != null) {
+            this.editingPostId = postId
             _uiState.update {
                 it.copy(
                     isEditMode = true,
                     pageText = page.toString(),
-                    isGeneralReview = isOverview,
+                    isGeneralReview = isOverview ?: false,
                     opinionTextFieldValue = TextFieldValue(
                         text = content,
                         selection = TextRange(content.length)
                     )
                 )
             }
         }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt
around lines 68–81, the current guard requires postId, page, content and
isOverview all non-null which wrongly rejects valid cases where isOverview is
false or content is an empty string; change the entry condition to only require
postId and page non-null, then inside the block treat isOverview and content
defensively (e.g., use isOverview ?: false for the boolean and content ?: ""
when populating TextFieldValue, and set selection based on the resolved content
length) while still setting isEditMode and postIdToEdit as before.

Comment on lines +144 to +160
private fun updateRecord() {
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true) }
val content = _uiState.value.opinionTextFieldValue.text
val postId = postIdToEdit ?: return@launch

roomsRepository.patchRoomsRecord(
roomId = roomId,
recordId = postId,
content = content
).onSuccess {
_uiState.update { it.copy(isLoading = false, isSuccess = true) }
}.onFailure { throwable ->
_uiState.update { it.copy(isLoading = false, error = throwable.message) }
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

에러 처리 로직 개선 필요

postIdToEdit가 null인 경우 return@launch로 조용히 종료하는 대신, 에러 상태를 업데이트해야 합니다.

     private fun updateRecord() {
         viewModelScope.launch {
             _uiState.update { it.copy(isLoading = true) }
             val content = _uiState.value.opinionTextFieldValue.text
-            val postId = postIdToEdit ?: return@launch
+            val postId = editingPostId
+            if (postId == null) {
+                _uiState.update { 
+                    it.copy(isLoading = false, error = "수정할 기록을 찾을 수 없습니다.")
+                }
+                return@launch
+            }

             roomsRepository.patchRoomsRecord(
                 roomId = roomId,
                 recordId = postId,
                 content = content
             ).onSuccess {
                 _uiState.update { it.copy(isLoading = false, isSuccess = true) }
             }.onFailure { throwable ->
                 _uiState.update { it.copy(isLoading = false, error = throwable.message) }
             }
         }
     }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt
around lines 144-160, the code silently returns when postIdToEdit is null which
leaves isLoading=true and no error state; change the logic to check postIdToEdit
before or immediately after setting isLoading and, if null, update _uiState to
set isLoading=false and set an appropriate error (e.g., "Missing postIdToEdit")
and return; ensure isSuccess is false in that branch so the UI reflects the
failure.

Comment on lines +100 to 101
refreshAllData()
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

async/await가 효과 없음 — 내부에서 다시 launch되어 대기되지 않습니다

loadPosts()loadBookPageInfo() 둘 다 내부에서 viewModelScope.launch를 사용하므로, 여기서 async { ... }로 감싸도 즉시 반환되어 awaitAll이 실질적으로 아무 것도 대기하지 않습니다. 의도와 달리 동시 실행/대기가 이뤄지지 않으므로 정정이 필요합니다.

권장 수정안 1(가장 간단): 두 함수를 직접 호출(이미 내부에서 launch).

-    private fun refreshAllData() {
-        viewModelScope.launch {
-            val postsJob = async { loadPosts(isRefresh = true) }
-            val bookPageJob = async { loadBookPageInfo() }
-            awaitAll(postsJob, bookPageJob)
-        }
-    }
+    private fun refreshAllData() {
+        // 두 함수가 자체적으로 viewModelScope.launch를 사용하므로 호출만으로 충분
+        loadPosts(isRefresh = true)
+        loadBookPageInfo()
+    }

권장 수정안 2(진짜 동시 실행/대기): 두 함수를 suspend로 바꾸고 내부 launch 제거 후 coroutineScope에서 launch/join 혹은 async/awaitAll로 병렬화.

추가 제안(선택): 새로고침 시 nextCursor를 명시적으로 초기화.

     private fun loadPosts(isRefresh: Boolean = false) {
         val currentState = _uiState.value
         if (currentState.isLoading || currentState.isLoadingMore || (currentState.isLastPage && !isRefresh)) return

         viewModelScope.launch {
+            if (isRefresh) nextCursor = null

Also applies to: 123-129

Comment on lines +151 to 166
private fun updateVote() {
val currentState = _uiState.value
val postId = currentState.postId ?: return

viewModelScope.launch {
_uiState.update { it.copy(isLoading = true) }
roomsRepository.patchRoomsVote(
roomId = roomId,
voteId = postId,
content = currentState.titleValue.text
).onSuccess {
_uiState.update { it.copy(isLoading = false, isSuccess = true) }
}.onFailure { throwable ->
_uiState.update { it.copy(isLoading = false, error = throwable.message) }
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

투표 수정 시 옵션 변경사항이 서버에 반영되지 않음

현재 patchRoomsVote 호출에 content만 전달합니다. 사용자가 옵션을 추가/삭제/수정해도 서버로 전송되지 않아 수정 내용이 유실됩니다. 또한 입력 검증과 상태 초기화도 필요합니다.

     private fun updateVote() {
         val currentState = _uiState.value
         val postId = currentState.postId ?: return
-
-        viewModelScope.launch {
-            _uiState.update { it.copy(isLoading = true) }
-            roomsRepository.patchRoomsVote(
-                roomId = roomId,
-                voteId = postId,
-                content = currentState.titleValue.text
-            ).onSuccess {
-                _uiState.update { it.copy(isLoading = false, isSuccess = true) }
-            }.onFailure { throwable ->
-                _uiState.update { it.copy(isLoading = false, error = throwable.message) }
-            }
-        }
+        // 간단 입력 검증: 제목 공백 및 옵션 2개 미만 방지
+        val filledOptionsCount = currentState.options.count { it.isNotBlank() }
+        if (currentState.titleValue.text.isBlank() || filledOptionsCount < 2) {
+            _uiState.update { it.copy(error = "제목과 최소 2개의 옵션을 입력해주세요.") }
+            return
+        }
+
+        val voteItems = currentState.options
+            .filter { it.isNotBlank() }
+            .map { VoteItem(itemName = it) }
+
+        viewModelScope.launch {
+            _uiState.update { it.copy(isLoading = true, isSuccess = false, error = null) }
+            roomsRepository.patchRoomsVote(
+                roomId = roomId,
+                voteId = postId,
+                content = currentState.titleValue.text,
+                voteItemList = voteItems
+            ).onSuccess {
+                _uiState.update { it.copy(isLoading = false, isSuccess = true) }
+            }.onFailure { throwable ->
+                _uiState.update { it.copy(isLoading = false, isSuccess = false, error = throwable.message) }
+            }
+        }
     }

다음 스크립트로 Repository/Service/Request 시그니처를 확인해 주세요. patchRoomsVote가 voteItemList(또는 유사 필드)를 지원하는지 점검이 필요합니다.


🏁 Script executed:

#!/bin/bash
# 1) patchRoomsVote 시그니처와 요청 모델 확인
rg -nP -C3 --type=kt -g '!**/build/**' '\bpatchRoomsVote\s*\('
fd -a -t f -e kt 'RoomsPatchVoteRequest|RoomsPatchVoteResponse|RoomsRepository|RoomsService|VoteItem' 2>/dev/null | sed 's|^|--- |'
rg -nP -C3 --type=kt -g '!**/build/**' '\bdata\s+class\s+RoomsPatchVoteRequest\b|\binterface\s+RoomsService\b|\bclass\s+RoomsRepository\b'

Length of output: 451


🏁 Script executed:

#!/bin/bash
# Inspect RoomsRepository for patchRoomsVote signature
echo "---- RoomsRepository.kt ----"
sed -n '1,200p' app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt
grep -nR "patchRoomsVote" -n app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt || true

# Inspect RoomsService for patchRoomsVote
echo "---- RoomsService.kt ----"
sed -n '1,200p' app/src/main/java/com/texthip/thip/data/service/RoomsService.kt
grep -nR "patchRoomsVote" -n app/src/main/java/com/texthip/thip/data/service/RoomsService.kt || true

# Inspect RoomsPatchVoteRequest to see available fields
echo "---- RoomsPatchVoteRequest.kt ----"
sed -n '1,200p' app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsPatchVoteRequest.kt

Length of output: 15045


patchRoomsVote에 옵션 목록 전송 로직 추가 필요
RoomsPatchVoteRequest에 변경된 옵션을 담을 voteItems(List) 필드를 추가하고, RoomsService.patchRoomsVote @patch 메서드와 RoomsRepository.patchRoomsVote 시그니처에 voteItems 파라미터를 반영한 뒤 ViewModel의 updateVote에서 전달하도록 수정하세요. 입력 검증(제목 빈값, 옵션 2개 미만) 로직은 그대로 유지해야 합니다.

@Nico1eKim Nico1eKim merged commit f555c37 into THIP-TextHip:develop Sep 11, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[API] 기록장 기록, 투표 수정 [(THIP2025-342)]

1 participant