Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.texthip.thip.data.model.rooms.request

import kotlinx.serialization.Serializable

@Serializable
data class RoomsPatchRecordRequest(
val content: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.texthip.thip.data.model.rooms.request

import kotlinx.serialization.Serializable

@Serializable
data class RoomsPatchVoteRequest(
val content: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.texthip.thip.data.model.rooms.response

import kotlinx.serialization.Serializable

@Serializable
data class RoomsPatchRecordResponse(
val roomId: Int
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.texthip.thip.data.model.rooms.response

import kotlinx.serialization.Serializable

@Serializable
data class RoomsPatchVoteResponse(
val roomId: Int,
)
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ data class PostList(
data class VoteItems(
val voteItemId: Int,
val itemName: String,
val percentage: Int,
val count: Int,
val isVoted: Boolean,
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import com.texthip.thip.data.model.rooms.request.RoomJoinRequest
import com.texthip.thip.data.model.rooms.request.RoomSecretRoomRequest
import com.texthip.thip.data.model.rooms.request.RoomsCreateDailyGreetingRequest
import com.texthip.thip.data.model.rooms.request.RoomsCreateVoteRequest
import com.texthip.thip.data.model.rooms.request.RoomsPatchRecordRequest
import com.texthip.thip.data.model.rooms.request.RoomsPatchVoteRequest
import com.texthip.thip.data.model.rooms.request.RoomsPostsLikesRequest
import com.texthip.thip.data.model.rooms.request.RoomsRecordRequest
import com.texthip.thip.data.model.rooms.request.RoomsVoteRequest
Expand Down Expand Up @@ -316,4 +318,32 @@ class RoomsRepository @Inject constructor(
roomId = roomId,
).handleBaseResponse().getOrThrow()
}

suspend fun patchRoomsRecord(
roomId: Int,
recordId: Int,
content: String
) = runCatching {
roomsService.patchRoomsRecord(
roomId = roomId,
recordId = recordId,
request = RoomsPatchRecordRequest(
content = content
)
).handleBaseResponse().getOrThrow()
}

suspend fun patchRoomsVote(
roomId: Int,
voteId: Int,
content: String,
) = runCatching {
roomsService.patchRoomsVote(
roomId = roomId,
voteId = voteId,
request = RoomsPatchVoteRequest(
content = content,
)
).handleBaseResponse().getOrThrow()
}
Comment on lines +336 to +348
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.

}
19 changes: 19 additions & 0 deletions app/src/main/java/com/texthip/thip/data/service/RoomsService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import com.texthip.thip.data.model.rooms.request.RoomJoinRequest
import com.texthip.thip.data.model.rooms.request.RoomSecretRoomRequest
import com.texthip.thip.data.model.rooms.request.RoomsCreateDailyGreetingRequest
import com.texthip.thip.data.model.rooms.request.RoomsCreateVoteRequest
import com.texthip.thip.data.model.rooms.request.RoomsPatchRecordRequest
import com.texthip.thip.data.model.rooms.request.RoomsPatchVoteRequest
import com.texthip.thip.data.model.rooms.request.RoomsPostsLikesRequest
import com.texthip.thip.data.model.rooms.request.RoomsRecordRequest
import com.texthip.thip.data.model.rooms.request.RoomsVoteRequest
Expand All @@ -24,6 +26,8 @@ import com.texthip.thip.data.model.rooms.response.RoomsDailyGreetingResponse
import com.texthip.thip.data.model.rooms.response.RoomsDeleteDailyGreetingResponse
import com.texthip.thip.data.model.rooms.response.RoomsDeleteRecordResponse
import com.texthip.thip.data.model.rooms.response.RoomsDeleteVoteResponse
import com.texthip.thip.data.model.rooms.response.RoomsPatchRecordResponse
import com.texthip.thip.data.model.rooms.response.RoomsPatchVoteResponse
import com.texthip.thip.data.model.rooms.response.RoomsPlayingResponse
import com.texthip.thip.data.model.rooms.response.RoomsPostsLikesResponse
import com.texthip.thip.data.model.rooms.response.RoomsPostsResponse
Expand All @@ -35,6 +39,7 @@ import com.texthip.thip.data.model.rooms.response.RoomsVoteResponse
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.PATCH
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query
Expand Down Expand Up @@ -195,4 +200,18 @@ interface RoomsService {
suspend fun leaveRoom(
@Path("roomId") roomId: Int
): BaseResponse<Unit>

@PATCH("rooms/{roomId}/records/{recordId}")
suspend fun patchRoomsRecord(
@Path("roomId") roomId: Int,
@Path("recordId") recordId: Int,
@Body request: RoomsPatchRecordRequest
): BaseResponse<RoomsPatchRecordResponse>

@PATCH("rooms/{roomId}/votes/{voteId}")
suspend fun patchRoomsVote(
@Path("roomId") roomId: Int,
@Path("voteId") voteId: Int,
@Body request: RoomsPatchVoteRequest
): BaseResponse<RoomsPatchVoteResponse>
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,19 @@ fun GroupVoteButton(
hasVoted: Boolean = false, // 투표 여부
onOptionSelected: (Int?) -> Unit
) {
val totalVotes = voteItems.sumOf { it.count }

Column(
modifier = modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
voteItems.forEachIndexed { index, item ->
val isSelected = selectedIndex == index
val votePercent = if (hasVoted) item.percentage.coerceIn(0, 100) else 0
val votePercent = if (totalVotes > 0) {
(item.count.toFloat() / totalVotes * 100).toInt()
} else {
0
}

val animatedPercent by animateFloatAsState(
targetValue = votePercent / 100f,
Expand Down Expand Up @@ -104,7 +110,7 @@ fun GroupVoteButton(
)
if (hasVoted) {
Text(
text = "${item.percentage}%",
text = "${item.count}표",
color = textColor,
style = fontStyle
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ fun BookPageTextField(
modifier: Modifier = Modifier,
bookTotalPage: Int,
enabled: Boolean = true,
readOnly: Boolean = false,
text: String,
isError: Boolean,
onValueChange: (String) -> Unit,
showClearButton: Boolean = true,
showTotalPage: Boolean = true
) {
var hasFocusCleared by remember(text) { mutableStateOf(false) }

Expand All @@ -62,23 +64,30 @@ fun BookPageTextField(
}
},
enabled = enabled,
visualTransformation = if (enabled) {
readOnly = readOnly,
visualTransformation = if (showTotalPage) {
SuffixTransformation("/${bookTotalPage}p", colors.Grey02)
} else {
VisualTransformation.None
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = modifier
.size(width = 320.dp, height = 48.dp)
.onFocusChanged { focusState ->
if (focusState.isFocused && !hasFocusCleared && text.isNotEmpty()) {
hasFocusCleared = true
onValueChange("")
}
if (!focusState.isFocused) {
hasFocusCleared = false
.then(
if (enabled) {
Modifier.onFocusChanged { focusState ->
if (focusState.isFocused && !hasFocusCleared && text.isNotEmpty()) {
hasFocusCleared = true
onValueChange("")
}
if (!focusState.isFocused) {
hasFocusCleared = false
}
}
} else {
Modifier
}
}
)
.then(
if (isError)
Modifier.border(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ fun BorderedTextField(
hint: String,
text: String,
canRemove: Boolean = true,
isEnabled: Boolean = true,
onTextChange: (String) -> Unit,
onDelete: () -> Unit,
onRemoveField: () -> Unit
Expand All @@ -46,11 +47,15 @@ fun BorderedTextField(
val interactionSource = remember { MutableInteractionSource() }
val isFocused by interactionSource.collectIsFocusedAsState()

val iconRes = when {
isFocused && text.isEmpty() -> R.drawable.ic_x_circle_darkgrey
isFocused && text.isNotEmpty() -> R.drawable.ic_x_circle_grey
!isFocused && canRemove -> R.drawable.ic_delete
else -> null
val iconRes = if (isEnabled) {
when {
isFocused && text.isEmpty() -> R.drawable.ic_x_circle_darkgrey
isFocused && text.isNotEmpty() -> R.drawable.ic_x_circle_grey
!isFocused && canRemove -> R.drawable.ic_delete
else -> null
}
} else {
null
}

val iconEnabled = when (iconRes) {
Expand All @@ -74,6 +79,7 @@ fun BorderedTextField(
BasicTextField(
value = text,
onValueChange = onTextChange,
enabled = isEnabled,
textStyle = myStyle.copy(color = colors.White),
singleLine = true,
modifier = Modifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.texthip.thip.R
Expand All @@ -25,13 +29,13 @@ import com.texthip.thip.ui.theme.ThipTheme.typography
@Composable
fun OpinionInputSection(
title: String = stringResource(R.string.my_opinion_title),
text: String,
onTextChange: (String) -> Unit,
textFieldValue: TextFieldValue,
onTextChange: (TextFieldValue) -> Unit,
hint: String = stringResource(R.string.my_opinion_placeholder),
maxLength: Int = 500
maxLength: Int = 500,
focusRequester: FocusRequester = remember { FocusRequester() }
) {
val isOverLimit = text.length > maxLength
val displayCount = if (text.length > maxLength) maxLength else text.length
val text = textFieldValue.text

Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
Text(
Expand All @@ -44,13 +48,16 @@ fun OpinionInputSection(
modifier = Modifier.fillMaxWidth()
) {
BasicTextField(
value = text,
onValueChange = {
if (it.length <= maxLength) onTextChange(it)
value = textFieldValue,
onValueChange = { newTextFieldValue ->
if (newTextFieldValue.text.length <= maxLength) {
onTextChange(newTextFieldValue)
}
},
textStyle = typography.menu_r400_s14_h24.copy(color = colors.White),
modifier = Modifier
.fillMaxWidth(),
.fillMaxWidth()
.focusRequester(focusRequester),
cursorBrush = SolidColor(colors.NeonGreen),
decorationBox = { innerTextField ->
Box(
Expand All @@ -74,8 +81,8 @@ fun OpinionInputSection(
horizontalArrangement = Arrangement.End
) {
Text(
text = stringResource(R.string.group_input_count, displayCount, maxLength),
color = if (isOverLimit) colors.Red else colors.NeonGreen,
text = stringResource(R.string.group_input_count, text.length, maxLength),
color = colors.NeonGreen,
style = typography.info_r400_s12
)
}
Expand All @@ -85,10 +92,12 @@ fun OpinionInputSection(
@Preview
@Composable
private fun OpinionInputSectionPreview() {
var text by rememberSaveable { mutableStateOf("") }
var value by rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue(""))
}

OpinionInputSection(
text = text,
onTextChange = { text = it }
textFieldValue = value,
onTextChange = { value = it }
)
}
Loading