Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.texthip.thip.data.model.rooms.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class RoomsSearchResponse(
@SerialName("roomList") val roomList: List<SearchRoomItem> = emptyList(),
@SerialName("nextCursor") val nextCursor: String? = null,
@SerialName("isLast") val isLast: Boolean = true
)

@Serializable
data class SearchRoomItem(
@SerialName("roomId") val roomId: Int = 0,
@SerialName("bookImageUrl") val bookImageUrl: String? = null,
@SerialName("roomName") val roomName: String = "",
@SerialName("memberCount") val memberCount: Int = 0,
@SerialName("recruitCount") val recruitCount: Int = 0,
@SerialName("deadlineDate") val deadlineDate: String = "",
@SerialName("isPublic") val isPublic: Boolean = true
)
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.texthip.thip.data.model.rooms.response.RoomCloseResponse
import com.texthip.thip.data.model.rooms.response.RoomMainList
import com.texthip.thip.data.model.rooms.response.RoomRecruitingResponse
import com.texthip.thip.data.model.rooms.response.RoomSecretRoomResponse
import com.texthip.thip.data.model.rooms.response.RoomsSearchResponse
import com.texthip.thip.data.service.RoomsService
import javax.inject.Inject
import javax.inject.Singleton
Expand Down Expand Up @@ -121,6 +122,19 @@ class RoomsRepository @Inject constructor(
response
}

/** 모임방 검색 */
suspend fun searchRooms(
keyword: String,
category: String,
sort: String = "deadline",
isFinalized: Boolean = false,
cursor: String? = null
): Result<RoomsSearchResponse?> = runCatching {
roomsService.searchRooms(keyword, category, sort, isFinalized, cursor)
.handleBaseResponse()
.getOrThrow()
}


/** 기록장 API들 */
suspend fun getRoomsPlaying(
Expand Down
11 changes: 11 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 @@ -28,6 +28,7 @@ import com.texthip.thip.data.model.rooms.response.RoomsPostsLikesResponse
import com.texthip.thip.data.model.rooms.response.RoomsPostsResponse
import com.texthip.thip.data.model.rooms.response.RoomsRecordResponse
import com.texthip.thip.data.model.rooms.response.RoomsRecordsPinResponse
import com.texthip.thip.data.model.rooms.response.RoomsSearchResponse
import com.texthip.thip.data.model.rooms.response.RoomsUsersResponse
import com.texthip.thip.data.model.rooms.response.RoomsVoteResponse
import retrofit2.http.Body
Expand Down Expand Up @@ -88,6 +89,16 @@ interface RoomsService {
@Path("roomId") roomId: Int
): BaseResponse<RoomCloseResponse>

/** 모임방 검색 */
@GET("rooms/search")
suspend fun searchRooms(
@Query("keyword") keyword: String,
@Query("category") category: String,
@Query("sort") sort: String = "deadline",
@Query("isFinalized") isFinalized: Boolean = false,
@Query("cursor") cursor: String? = null
): BaseResponse<RoomsSearchResponse>



/** 기록장 API들 */
Expand Down
21 changes: 9 additions & 12 deletions app/src/main/java/com/texthip/thip/ui/common/cards/CardItemRoom.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
Expand All @@ -39,7 +38,7 @@ fun CardItemRoom(
participants: Int,
maxParticipants: Int,
isRecruiting: Boolean,
endDate: Int? = null,
endDate: String? = null,
imageUrl: String? = null,
hasBorder: Boolean = false,
onClick: () -> Unit = {}
Expand Down Expand Up @@ -83,8 +82,8 @@ fun CardItemRoom(

Column(
modifier = Modifier
.fillMaxWidth()
.height(107.dp),
.fillMaxWidth()
.height(107.dp),
verticalArrangement = Arrangement.Center
) {
Text(
Expand Down Expand Up @@ -151,10 +150,8 @@ fun CardItemRoom(
Spacer(modifier = Modifier.height(5.dp))

Text(
text = stringResource(
R.string.card_item_end_date,
endDate
) + if (isRecruiting) stringResource(
text = endDate
+ if (isRecruiting) stringResource(
R.string.card_item_end
) else stringResource(R.string.card_item_finish),

Expand All @@ -180,23 +177,23 @@ fun CardItemRoomPreview() {
participants = 22,
maxParticipants = 30,
isRecruiting = true,
endDate = 3,
endDate = "3일 뒤",
imageUrl = null
)
CardItemRoom(
title = "모임방 이름입니다. 모임방 이름입니다.",
participants = 22,
maxParticipants = 30,
isRecruiting = false,
endDate = 3,
endDate = "3",
imageUrl = null
)
CardItemRoom(
title = "모임방 이름입니다. 모임방 이름입니다.",
participants = 22,
maxParticipants = 30,
isRecruiting = true,
endDate = 3,
endDate = "3",
imageUrl = null,
hasBorder = true
)
Expand All @@ -205,7 +202,7 @@ fun CardItemRoomPreview() {
participants = 22,
maxParticipants = 30,
isRecruiting = false,
endDate = 3,
endDate = "3",
imageUrl = null,
hasBorder = true
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
Expand All @@ -40,7 +39,7 @@ fun CardItemRoomSmall(
title: String,
participants: Int,
maxParticipants: Int,
endDate: Int?,
endDate: String?,
imageUrl: String? = null, // API에서 받은 이미지 URL
isWide: Boolean = false,
isSecret: Boolean = false,
Expand Down Expand Up @@ -146,7 +145,7 @@ fun CardItemRoomSmall(

endDate?.let {
Text(
text = stringResource(R.string.card_item_end_date_recruit, endDate),
text = endDate + stringResource(R.string.card_item_end),
Copy link

Copilot AI Aug 18, 2025

Choose a reason for hiding this comment

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

String concatenation for time display formatting should be handled consistently. Consider using string formatting or a utility function to ensure proper formatting across all time displays.

Suggested change
text = endDate + stringResource(R.string.card_item_end),
text = stringResource(R.string.card_item_end, endDate),

Copilot uses AI. Check for mistakes.
color = colors.Red,
style = typography.menu_sb600_s12_h20
)
Expand All @@ -167,14 +166,14 @@ fun CardItemRoomSmallPreview() {
title = "방 제목입니다 방 제목입니다",
participants = 22,
maxParticipants = 30,
endDate = 3
endDate = "3일 뒤"
)

CardItemRoomSmall(
title = "와이드 카드 fillMaxWidth",
participants = 18,
maxParticipants = 25,
endDate = 5,
endDate = "5",
isWide = true
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,14 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.texthip.thip.R
import com.texthip.thip.ui.common.buttons.GenreChipRow
import com.texthip.thip.ui.common.cards.CardItemRoom
import com.texthip.thip.data.model.rooms.response.RoomMainList
import com.texthip.thip.data.manager.Genre
import com.texthip.thip.data.model.rooms.response.RoomMainList
import com.texthip.thip.data.model.rooms.response.RoomMainResponse
import com.texthip.thip.ui.common.buttons.GenreChipRow
import com.texthip.thip.ui.common.cards.CardItemRoom
import com.texthip.thip.ui.theme.ThipTheme
import com.texthip.thip.ui.theme.ThipTheme.colors
import com.texthip.thip.ui.theme.ThipTheme.typography
import com.texthip.thip.utils.rooms.DateUtils
import com.texthip.thip.utils.rooms.toDisplayStrings

@SuppressLint("UnusedBoxWithConstraintsScope")
Expand Down Expand Up @@ -68,11 +67,17 @@ fun GroupRoomDeadlineSection(

// Genre enum을 현지화된 문자열로 변환
val genreStrings = Genre.entries.toDisplayStrings()

// 마감 임박 방 목록과 인기 방 목록을 섹션으로 구성
val roomSections = listOf(
Pair(stringResource(R.string.room_section_deadline), roomMainList?.deadlineRoomList ?: emptyList()),
Pair(stringResource(R.string.room_section_popular), roomMainList?.popularRoomList ?: emptyList())
Pair(
stringResource(R.string.room_section_deadline),
roomMainList?.deadlineRoomList ?: emptyList()
),
Pair(
stringResource(R.string.room_section_popular),
roomMainList?.popularRoomList ?: emptyList()
)
)

val effectivePagerState = rememberPagerState(
Expand Down Expand Up @@ -188,20 +193,19 @@ fun GroupRoomDeadlineSection(
) {
rooms.forEach { room ->
// RoomMainResponse를 CardItemRoom에 맞게 변환
val daysLeft = DateUtils.extractDaysFromDeadline(room.deadlineDate)
CardItemRoom(
title = room.roomName,
participants = room.memberCount,
maxParticipants = room.recruitCount,
isRecruiting = true, // RoomMainResponse에는 모집중인 방만 있음
endDate = daysLeft,
endDate = room.deadlineDate,
imageUrl = room.bookImageUrl,
onClick = { onRoomClick(room) },
hasBorder = true,
)
}
}

if (rooms.size < 4) {
Spacer(
modifier = Modifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ data class GroupCardItemRoomData(
val participants: Int,
val maxParticipants: Int,
val isRecruiting: Boolean,
val endDate: Int? = null, // 남은 일 수
val endDate: String? = null, // 마감 시간 텍스트 (예: "8시간 뒤")
val imageUrl: String? = null, // API에서 받은 이미지 URL
val isSecret: Boolean = false
)


)
Comment on lines +9 to +12
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

endDate의 타입 변경(Int? → String?) 일관성 좋습니다

UI 전반이 문자열 기반 마감 표기로 이행되면서 본 데이터 모델 수정이 방향성에 부합합니다.

레거시(Int) 타입이나 변환 유틸 의존이 남아있는지 확인 부탁드립니다:


🏁 Script executed:

#!/bin/bash
# endDate가 Int로 선언/사용되는 곳 탐지
rg -nP -C2 '\bendDate\s*:\s*Int\??' --type=kt

# 남은 일수 계산 유틸 호출 탐지
rg -nP -C2 'getEndDateInDays|DateUtils' --type=kt

Length of output: 153


endDate의 Int 타입 선언/사용 및 변환 유틸 호출 여부를 Kotlin 파일 전역에서 재검증합니다. 다음 스크립트를 실행해주세요:


🏁 Script executed:

#!/bin/bash
# endDate가 Int로 선언/사용되는 곳 탐지 (Kotlin 파일)
rg -nP -C2 '\bendDate\s*:\s*Int\??' -g '*.kt'

# 남은 일수 계산 유틸 호출 탐지 (Kotlin 파일)
rg -nP -C2 'getEndDateInDays|DateUtils' -g '*.kt'

Length of output: 577


endDate 변환 유틸리티 정비 필요

String?으로 변경된 endDate 타입에 맞춰, 기존의 정수형 변환 유틸(getEndDateInDays)이 아직 남아 있습니다. 해당 메서드가 더 이상 사용되지 않거나, 새로운 문자열 기반 로직으로 대체되어야 합니다.

수정 대상:

  • app/src/main/java/com/texthip/thip/ui/group/done/mock/MyRoomCardData.kt
    • fun MyRoomCardData.getEndDateInDays(): Int { … }

조치:

  • 해당 메서드를 제거하거나
  • 문자열 파싱 로직으로 업데이트하여 코드베이스 전체에서 일관된 String 타입 처리 보장
🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/group/myroom/mock/GroupCardItemRoomData.kt
around lines 9 to 12, the endDate field was changed to String? but the old
integer-based utility getEndDateInDays() in
app/src/main/java/com/texthip/thip/ui/group/done/mock/MyRoomCardData.kt still
assumes an Int; remove that obsolete method or replace its logic with a
String-based parser that accepts the nullable endDate string, safely handles
null/empty values, parses formats used by the API (e.g., “8시간 뒤”, ISO
timestamps, or relative strings), and returns a consistent Int result (or change
callers to expect String), updating all usages across the codebase to use the
new string-handling utility or removed method accordingly.

Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fun GroupMyScreen(
viewModel: GroupMyViewModel = hiltViewModel()
) {
val uiState by viewModel.uiState.collectAsState()

GroupMyContent(
uiState = uiState,
onCardClick = onCardClick,
Expand All @@ -69,7 +69,7 @@ fun GroupMyContent(
onChangeRoomType: (RoomType) -> Unit = {}
) {
val listState = rememberLazyListState()

// 무한 스크롤 로직
val shouldLoadMore by remember(uiState.canLoadMore, uiState.isLoadingMore) {
derivedStateOf {
Expand All @@ -78,13 +78,13 @@ fun GroupMyContent(
uiState.canLoadMore && !uiState.isLoadingMore && totalItems > 0 && lastVisibleIndex >= totalItems - 3
}
}

LaunchedEffect(shouldLoadMore) {
if (shouldLoadMore) {
onLoadMore()
}
}

// Filter 상태를
val selectedStates = remember(uiState.currentRoomType) {
when (uiState.currentRoomType) {
Expand All @@ -102,7 +102,7 @@ fun GroupMyContent(
title = stringResource(R.string.my_group_room),
onLeftClick = onNavigateBack,
)

PullToRefreshBox(
isRefreshing = uiState.isLoading,
onRefresh = onRefresh,
Expand Down Expand Up @@ -138,6 +138,7 @@ fun GroupMyContent(
RoomType.RECRUITING
}
}

else -> RoomType.PLAYING_AND_RECRUITING
}
onChangeRoomType(newRoomType)
Expand All @@ -159,7 +160,7 @@ fun GroupMyContent(
participants = room.memberCount,
maxParticipants = room.recruitCount,
isRecruiting = RoomUtils.isRecruitingByType(room.type),
endDate = RoomUtils.getEndDateInDays(room.endDate),
endDate = room.endDate,
imageUrl = room.bookImageUrl,
onClick = { onCardClick(room) }
)
Comment on lines +163 to 166
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

endDate를 문자열로 직접 전달하도록 변경된 점 확인(LGTM) + 전파 영향 검증 요청

CardItemRoom의 endDate가 String?로 이관되었다면 본 변경은 타당합니다. 남은 참조 중 Int 기반(endDate: Int? 또는 변환 유틸) 사용이 없는지 전체 코드베이스 검증을 권장합니다.

다음 스크립트로 시그니처/잔존 참조를 확인해주세요:


🏁 Script executed:

#!/bin/bash
# CardItemRoom의 endDate 파라미터 타입이 String?인지 확인
rg -nP -C3 'fun\s+CardItemRoom\s*\([^)]*endDate\s*:\s*String\?' --type=kt

# endDate를 Int로 사용하는 잔존 호출/정의 탐지
rg -nP -C2 '\bendDate\s*:\s*Int\??' --type=kt

# 삭제/미사용이어야 할 과거 유틸 호출 탐지
rg -nP -C2 'getEndDateInDays|DateUtils' --type=kt

Length of output: 256


파일 타입 인식 에러로 --type=kt 대신 glob 패턴을 사용해 .kt 파일에서 재검증합니다.


🏁 Script executed:

#!/bin/bash
# CardItemRoom의 endDate 파라미터 타입이 String?인지 확인
rg -nP -C3 'fun\s+CardItemRoom\s*\([^)]*endDate\s*:\s*String\?' -g '*.kt'

# endDate를 Int로 사용하는 잔존 호출/정의 탐지
rg -nP -C2 '\bendDate\s*:\s*Int\??' -g '*.kt'

# 삭제/미사용이어야 할 과거 유틸 호출 탐지
rg -nP -C2 'getEndDateInDays|DateUtils' -g '*.kt'

Length of output: 653


endDate를 String?으로 이관 확인 완료, 구 변환 유틸 제거 필요

CardItemRoom의 endDate 파라미터가 정상적으로 String?로 변경되었으나, mock 데이터 클래스에 여전히 Int 변환용 유틸(getEndDateInDays())이 남아 있습니다. 코드 정합성을 위해 해당 메서드 및 호출부를 제거하거나 String? 기반 로직으로 대체해주세요.

  • app/src/main/java/com/texthip/thip/ui/group/done/mock/MyRoomCardData.kt:32-34 (fun getEndDateInDays())
🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupMyScreen.kt
around lines 163-166 and
app/src/main/java/com/texthip/thip/ui/group/done/mock/MyRoomCardData.kt around
lines 32-34: the CardItemRoom now accepts endDate as String?, but the mock still
exposes an Int conversion util getEndDateInDays() and/or calls it; remove the
getEndDateInDays() function from MyRoomCardData and eliminate any calls to it,
and update the mock data to supply endDate as a nullable String (formatted the
same way production code expects) so the screen passes room.endDate directly to
CardItemRoom without converting to Int.

Expand Down
Loading