From bc500ca3d23981f54f9791156675d1346178dad3 Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Sun, 26 Oct 2025 21:50:50 +0900 Subject: [PATCH 01/22] =?UTF-8?q?[feat]:=20GroupMakeRoomRequest=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../group/makeroom/mock/GroupMakeRoomRequest.kt | 15 --------------- .../makeroom/viewmodel/GroupMakeRoomUiState.kt | 15 --------------- 2 files changed, 30 deletions(-) delete mode 100644 app/src/main/java/com/texthip/thip/ui/group/makeroom/mock/GroupMakeRoomRequest.kt diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/mock/GroupMakeRoomRequest.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/mock/GroupMakeRoomRequest.kt deleted file mode 100644 index b41d0b8b..00000000 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/mock/GroupMakeRoomRequest.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.texthip.thip.ui.group.makeroom.mock - -import java.time.LocalDate - -data class GroupMakeRoomRequest( - val selectedBook: BookData?, - val genreIndex: Int, - val roomTitle: String, - val roomDescription: String, - val meetingStartDate: LocalDate, - val meetingEndDate: LocalDate, - val memberLimit: Int, - val isPrivate: Boolean, - val password: String = "" -) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomUiState.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomUiState.kt index f3fa81a9..bcf5d342 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomUiState.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomUiState.kt @@ -2,7 +2,6 @@ package com.texthip.thip.ui.group.makeroom.viewmodel import com.texthip.thip.data.manager.Genre import com.texthip.thip.ui.group.makeroom.mock.BookData -import com.texthip.thip.ui.group.makeroom.mock.GroupMakeRoomRequest import java.time.LocalDate import java.time.temporal.ChronoUnit @@ -57,18 +56,4 @@ data class GroupMakeRoomUiState( isCountValid && isPasswordValid - // 서버 전송용 데이터로 변환 - fun toRequest(): GroupMakeRoomRequest { - return GroupMakeRoomRequest( - selectedBook = selectedBook, - genreIndex = selectedGenreIndex, - roomTitle = roomTitle.trim(), - roomDescription = roomDescription.trim(), - meetingStartDate = meetingStartDate, - meetingEndDate = meetingEndDate, - memberLimit = memberLimit, - isPrivate = isPrivate, - password = if (isPrivate) password else "" - ) - } } \ No newline at end of file From ae4a546facb52713a671e238549e916a95c64e30 Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Sun, 26 Oct 2025 21:52:59 +0900 Subject: [PATCH 02/22] =?UTF-8?q?[feat]:=20Response=20500=EC=97=90?= =?UTF-8?q?=EC=84=9C=EB=8F=84=20=EB=8B=A4=EC=8B=9C=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=ED=99=94=EB=A9=B4=EC=9C=BC=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=ED=95=98=EA=B2=8C=20AuthInterceptor=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/utils/auth/AuthInterceptor.kt | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/utils/auth/AuthInterceptor.kt b/app/src/main/java/com/texthip/thip/utils/auth/AuthInterceptor.kt index 8d5865e4..5cb37e51 100644 --- a/app/src/main/java/com/texthip/thip/utils/auth/AuthInterceptor.kt +++ b/app/src/main/java/com/texthip/thip/utils/auth/AuthInterceptor.kt @@ -28,9 +28,12 @@ class AuthInterceptor @Inject constructor( }.build() val response = chain.proceed(newRequest) - - // 401 응답 처리 - if (response.code == 401) { + + // 401 또는 인증 관련 500 에러 처리 + val shouldClearAuth = response.code == 401 || + (response.code == 500 && isAuthError(response)) + + if (shouldClearAuth) { runBlocking { tokenManager.clearTokens() authStateManager.triggerTokenExpired() @@ -39,4 +42,14 @@ class AuthInterceptor @Inject constructor( return response } + + private fun isAuthError(response: Response): Boolean { + return try { + val body = response.peekBody(Long.MAX_VALUE).string() + // 에러 코드 40108 (인증 처리 중 서버 오류) 확인 + body.contains("40108") + } catch (e: Exception) { + false + } + } } From 3233a9e07edbf89f779dc2b72af78cd7758c3c75 Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Sun, 26 Oct 2025 21:54:46 +0900 Subject: [PATCH 03/22] =?UTF-8?q?[refactor]:=20drawVerticalScrollbar=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/ui/common/modal/ScrollbarUtil.kt | 59 ++++++++++++++++++- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/common/modal/ScrollbarUtil.kt b/app/src/main/java/com/texthip/thip/ui/common/modal/ScrollbarUtil.kt index 40a7978a..713ceb3c 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/modal/ScrollbarUtil.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/modal/ScrollbarUtil.kt @@ -1,14 +1,13 @@ package com.texthip.thip.ui.common.modal import androidx.compose.foundation.ScrollState -import androidx.compose.runtime.Composable +import androidx.compose.foundation.lazy.LazyListState import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -26,6 +25,62 @@ fun Modifier.drawVerticalScrollbar( val scrollProgress = scrollState.value.toFloat() / scrollState.maxValue val scrollbarOffsetY = (size.height - scrollbarHeight) * scrollProgress + //전체 고정 바 + drawRoundRect( + color = trackColor, + topLeft = Offset(x = size.width - trackThickness.toPx(), y = 0f), + size = Size(trackThickness.toPx(), size.height), + cornerRadius = CornerRadius(trackThickness.toPx() / 2) + ) + //핸들 바 + drawRoundRect( + color = thumbColor, + topLeft = Offset(x = size.width - thumbThickness.toPx(), y = scrollbarOffsetY), + size = Size(thumbThickness.toPx(), scrollbarHeight), + cornerRadius = CornerRadius(thumbThickness.toPx() / 2) + ) + } +) + +fun Modifier.drawVerticalScrollbar( + lazyListState: LazyListState, + trackThickness: Dp = 1.dp, + thumbThickness: Dp = 3.dp, + trackColor: Color = Color.White.copy(alpha = 0.2f), + thumbColor: Color = Color.White.copy(alpha = 0.8f), +): Modifier = this.then( + Modifier.drawBehind { + val layoutInfo = lazyListState.layoutInfo + val totalItemsCount = layoutInfo.totalItemsCount + val viewportHeight = layoutInfo.viewportEndOffset - layoutInfo.viewportStartOffset + + // 스크롤이 필요 없으면 스크롤바 표시 안 함 + if (totalItemsCount == 0) return@drawBehind + if (!lazyListState.canScrollForward && !lazyListState.canScrollBackward) return@drawBehind + + val scrollbarHeight = size.height / 8f + + // 전체 컨텐츠 높이 추정 + val averageItemHeight = if (layoutInfo.visibleItemsInfo.isNotEmpty()) { + layoutInfo.visibleItemsInfo.map { it.size }.average().toFloat() + } else { + 100f + } + val estimatedTotalHeight = averageItemHeight * totalItemsCount + + // 스크롤 진행률 계산 + val firstVisibleIndex = lazyListState.firstVisibleItemIndex.toFloat() + val firstVisibleOffset = lazyListState.firstVisibleItemScrollOffset.toFloat() + + val scrollProgress = if (estimatedTotalHeight > 0) { + ((firstVisibleIndex * averageItemHeight + firstVisibleOffset) / estimatedTotalHeight) + .coerceIn(0f, 1f) + } else { + 0f + } + + val scrollbarOffsetY = (size.height - scrollbarHeight) * scrollProgress + //전체 고정 바 drawRoundRect( color = trackColor, From f10150dd0fedaf1e66b9cec9e7bf2ef60f96972c Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Sun, 26 Oct 2025 21:55:32 +0900 Subject: [PATCH 04/22] =?UTF-8?q?[refactor]:=20=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EA=B3=BC=20=EB=AA=A8=EC=9E=84=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=97=90=EC=84=9C=EC=9D=98=20=EC=B1=85=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=99=94=EB=A9=B4=EC=97=90=EC=84=9C=20=EB=B0=94?= =?UTF-8?q?=ED=85=80=EC=8B=9C=ED=8A=B8=20=EC=88=98=EC=A0=95=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/GroupBookListWithScrollbar.kt | 5 ++-- .../component/GroupBookSearchBottomSheet.kt | 24 +++++++++++++------ .../makeroom/screen/GroupMakeRoomScreen.kt | 7 +++--- .../viewmodel/GroupMakeRoomViewModel.kt | 8 +++---- 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookListWithScrollbar.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookListWithScrollbar.kt index bcf37098..d24206e3 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookListWithScrollbar.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookListWithScrollbar.kt @@ -4,13 +4,13 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.foundation.rememberScrollState import androidx.compose.material3.CircularProgressIndicator import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf @@ -57,7 +57,8 @@ fun GroupBookListWithScrollbar( state = listState, modifier = Modifier .fillMaxWidth() - .drawVerticalScrollbar(rememberScrollState()) + .fillMaxHeight() + .drawVerticalScrollbar(listState) ) { items(books) { book -> CardBookSearch( diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt index d6d14f5b..533545d3 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt @@ -49,16 +49,22 @@ fun GroupBookSearchBottomSheet( onSearch: (String) -> Unit = {}, onLoadMoreSaved: () -> Unit = {}, onLoadMoreGroup: () -> Unit = {}, - onLoadMoreSearch: () -> Unit = {} + onLoadMoreSearch: () -> Unit = {}, + showGroupBooksTab: Boolean = true ) { var selectedTab by rememberSaveable { mutableIntStateOf(0) } - val tabs = listOf( - stringResource(R.string.group_saved_book), stringResource(R.string.group_book) - ) + val tabs = if (showGroupBooksTab) { + listOf( + stringResource(R.string.group_saved_book), + stringResource(R.string.group_book) + ) + } else { + listOf(stringResource(R.string.group_saved_book)) + } var searchText by rememberSaveable { mutableStateOf("") } - val currentBooks = if (selectedTab == 0) savedBooks else groupBooks + val currentBooks = if (showGroupBooksTab && selectedTab == 1) groupBooks else savedBooks // 검색어가 있으면 검색 결과 사용, 없으면 탭별 도서 목록 사용 val displayBooks = if (searchText.isNotEmpty()) { @@ -120,7 +126,11 @@ fun GroupBookSearchBottomSheet( } else -> { - Column(Modifier.padding(horizontal = 20.dp)) { + Column( + Modifier + .weight(1f) + .padding(horizontal = 20.dp) + ) { when { searchText.isNotEmpty() -> { GroupBookListWithScrollbar( @@ -131,7 +141,7 @@ fun GroupBookSearchBottomSheet( onLoadMore = onLoadMoreSearch ) } - selectedTab == 0 -> { + !showGroupBooksTab || selectedTab == 0 -> { GroupBookListWithScrollbar( books = displayBooks, onBookClick = onBookSelect, diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt index 60202284..c55a13cb 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt @@ -85,7 +85,6 @@ fun GroupMakeRoomScreen( onUpdatePassword = viewModel::updatePassword, onSearchBooks = viewModel::searchBooks, onLoadMoreSavedBooks = viewModel::loadMoreSavedBooks, - onLoadMoreGroupBooks = viewModel::loadMoreGroupBooks, onLoadMoreSearchResults = viewModel::loadMoreSearchResults, modifier = modifier ) @@ -108,7 +107,6 @@ fun GroupMakeRoomContent( onUpdatePassword: (String) -> Unit = {}, onSearchBooks: (String) -> Unit = {}, onLoadMoreSavedBooks: () -> Unit = {}, - onLoadMoreGroupBooks: () -> Unit = {}, onLoadMoreSearchResults: () -> Unit = {} ) { val scrollState = rememberScrollState() @@ -272,8 +270,9 @@ fun GroupMakeRoomContent( hasMoreSearch = !uiState.isLastSearchPage, onSearch = onSearchBooks, onLoadMoreSaved = onLoadMoreSavedBooks, - onLoadMoreGroup = onLoadMoreGroupBooks, - onLoadMoreSearch = onLoadMoreSearchResults + onLoadMoreGroup = {}, + onLoadMoreSearch = onLoadMoreSearchResults, + showGroupBooksTab = false ) } } diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt index 9c1320e4..8dac044f 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt @@ -90,7 +90,7 @@ class GroupMakeRoomViewModel @Inject constructor( private fun loadBooks() { updateState { it.copy(isLoadingBooks = true) } loadSavedBooks(isInitial = true) - loadGroupBooks(isInitial = true) + // 모임 생성 화면에서는 저장된 책만 표시 } fun loadSavedBooks(isInitial: Boolean = false) { @@ -133,11 +133,11 @@ class GroupMakeRoomViewModel @Inject constructor( } } finally { isLoadingSavedBooks = false - updateState { + updateState { it.copy( - isLoadingBooks = if (isInitial && !isLoadingGroupBooks) false else it.isLoadingBooks, + isLoadingBooks = false, isLoadingMoreSavedBooks = false - ) + ) } } } From 34f50ad2dc44868a92094bb14e729dbc52baa77e Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Sun, 26 Oct 2025 22:12:54 +0900 Subject: [PATCH 05/22] =?UTF-8?q?[refactor]:=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EB=AA=A8=EC=9E=84=20=EC=83=9D=EC=84=B1=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/GroupBookSearchBottomSheet.kt | 5 +- .../makeroom/screen/GroupMakeRoomScreen.kt | 20 +------ .../viewmodel/GroupMakeRoomUiState.kt | 3 - .../viewmodel/GroupMakeRoomViewModel.kt | 56 ------------------- 4 files changed, 7 insertions(+), 77 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt index 533545d3..8cf59dee 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt @@ -28,6 +28,7 @@ import com.texthip.thip.ui.group.makeroom.mock.BookData import com.texthip.thip.ui.group.makeroom.mock.dummyGroupBooks import com.texthip.thip.ui.group.makeroom.mock.dummySavedBooks import com.texthip.thip.ui.theme.ThipTheme +import com.texthip.thip.ui.theme.ThipTheme.colors import com.texthip.thip.utils.rooms.advancedImePadding @Composable @@ -121,7 +122,9 @@ fun GroupBookSearchBottomSheet( .align(Alignment.CenterHorizontally), contentAlignment = Alignment.Center ) { - CircularProgressIndicator() + CircularProgressIndicator( + color = colors.White + ) } } diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt index c55a13cb..3f63d87d 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt @@ -258,15 +258,15 @@ fun GroupMakeRoomContent( onToggleBookSearchSheet(false) }, savedBooks = uiState.savedBooks, - groupBooks = uiState.groupBooks, + groupBooks = emptyList(), searchResults = uiState.searchResults, isLoading = uiState.isLoadingBooks, isSearching = uiState.isSearching, isLoadingMoreSaved = uiState.isLoadingMoreSavedBooks, - isLoadingMoreGroup = uiState.isLoadingMoreGroupBooks, + isLoadingMoreGroup = false, isLoadingMoreSearch = uiState.isLoadingMoreSearchResults, hasMoreSaved = !uiState.isLastSavedBooks, - hasMoreGroup = !uiState.isLastGroupBooks, + hasMoreGroup = false, hasMoreSearch = !uiState.isLastSearchPage, onSearch = onSearchBooks, onLoadMoreSaved = onLoadMoreSavedBooks, @@ -311,20 +311,6 @@ private fun GroupMakeRoomScreenPreview() { author = "유발 하라리", isbn = "9788934972464" ) - ), - groupBooks = listOf( - BookData( - title = "1984", - imageUrl = "https://picsum.photos/300/400?4", - author = "조지 오웰", - isbn = "9788937460777" - ), - BookData( - title = "어린왕자", - imageUrl = "https://picsum.photos/300/400?5", - author = "생텍쥐페리", - isbn = "9788932917245" - ) ) ) ) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomUiState.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomUiState.kt index bcf5d342..212b4416 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomUiState.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomUiState.kt @@ -19,12 +19,9 @@ data class GroupMakeRoomUiState( val isLoading: Boolean = false, val errorMessage: String? = null, val savedBooks: List = emptyList(), - val groupBooks: List = emptyList(), val isLoadingBooks: Boolean = false, val isLoadingMoreSavedBooks: Boolean = false, - val isLoadingMoreGroupBooks: Boolean = false, val isLastSavedBooks: Boolean = false, - val isLastGroupBooks: Boolean = false, val searchResults: List = emptyList(), val isSearching: Boolean = false, val isLoadingMoreSearchResults: Boolean = false, diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt index 8dac044f..554abe9a 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt @@ -36,9 +36,7 @@ class GroupMakeRoomViewModel @Inject constructor( private var searchJob: Job? = null private var loadMoreSearchJob: Job? = null private var savedBooksCursor: String? = null - private var groupBooksCursor: String? = null private var isLoadingSavedBooks = false - private var isLoadingGroupBooks = false companion object { private val DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy.MM.dd") @@ -143,64 +141,10 @@ class GroupMakeRoomViewModel @Inject constructor( } } - fun loadGroupBooks(isInitial: Boolean = false) { - if (isLoadingGroupBooks) return - if (!isInitial && _uiState.value.isLastGroupBooks) return - - viewModelScope.launch { - try { - isLoadingGroupBooks = true - - if (isInitial) { - updateState { it.copy(groupBooks = emptyList(), isLastGroupBooks = false) } - groupBooksCursor = null - } else { - updateState { it.copy(isLoadingMoreGroupBooks = true) } - } - - val cursor = if (isInitial) null else groupBooksCursor - - bookRepository.getBooks("JOINING", cursor) - .onSuccess { response -> - if (response != null) { - val currentList = if (isInitial) emptyList() else _uiState.value.groupBooks - val newBooks = response.bookList.map { it.toBookData() } - updateState { - it.copy( - groupBooks = currentList + newBooks, - isLastGroupBooks = response.isLast - ) - } - groupBooksCursor = response.nextCursor - } else { - updateState { it.copy(isLastGroupBooks = true) } - } - } - .onFailure { exception -> - if (isInitial) { - updateState { it.copy(groupBooks = emptyList()) } - } - } - } finally { - isLoadingGroupBooks = false - updateState { - it.copy( - isLoadingBooks = if (isInitial && !isLoadingSavedBooks) false else it.isLoadingBooks, - isLoadingMoreGroupBooks = false - ) - } - } - } - } - fun loadMoreSavedBooks() { loadSavedBooks(isInitial = false) } - fun loadMoreGroupBooks() { - loadGroupBooks(isInitial = false) - } - private fun BookSavedResponse.toBookData(): BookData { return BookData( title = this.bookTitle, From fe231e0bff31250a582199894a49be3bdcff5475 Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Sun, 26 Oct 2025 22:18:51 +0900 Subject: [PATCH 06/22] =?UTF-8?q?[refactor]:=20=EB=AA=A8=EC=9E=84=20?= =?UTF-8?q?=EC=9D=B8=EC=9B=90=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/ui/group/makeroom/component/GroupMemberLimitPicker.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupMemberLimitPicker.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupMemberLimitPicker.kt index 7b6437c8..cbca8943 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupMemberLimitPicker.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupMemberLimitPicker.kt @@ -28,7 +28,7 @@ fun GroupMemberLimitPicker( selectedCount: Int = 30, onCountSelected: (Int) -> Unit = { } ) { - val memberCounts = remember { (1..30).toList() } + val memberCounts = remember { (2..30).toList() } Column( modifier = modifier.fillMaxWidth() From fbfb4434df7c07e1bb7966d1c595cdb16eaf422a Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Mon, 27 Oct 2025 17:25:42 +0900 Subject: [PATCH 07/22] =?UTF-8?q?[refactor]:=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EB=AA=A8=EC=9E=84=20=ED=99=9C=EB=8F=99=EA=B8=B0=EA=B0=84=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=88=98=EC=A0=95=20(#1?= =?UTF-8?q?50)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/GroupRoomDurationPicker.kt | 56 ++++++++----------- .../makeroom/util/WheelPickerDisplayUtils.kt | 26 +++++++++ 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt index ff2b2376..0bb07ed2 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt @@ -21,6 +21,7 @@ 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.group.makeroom.util.WheelPickerUtils import com.texthip.thip.ui.theme.ThipTheme import com.texthip.thip.ui.theme.ThipTheme.colors import com.texthip.thip.ui.theme.ThipTheme.typography @@ -50,46 +51,35 @@ fun GroupRoomDurationPicker( } } - // 날짜 범위 계산 - val daysBetween = ChronoUnit.DAYS.between(startDate, endDate) - val isOverLimit = daysBetween > 91 - - // 날짜 선택 콜백 + // 날짜 유효성 검사 및 자동 조정 (통합) LaunchedEffect(startDate, endDate) { - if (endDate.isAfter(startDate)) { - onDateRangeSelected(startDate, endDate) - } - } + val (validatedStart, validatedEnd) = WheelPickerUtils.validateDateRange( + startDate = startDate, + endDate = endDate, + minDate = tomorrow, + maxDate = maxDate + ) - // 날짜 유효성 검사 및 자동 조정 - LaunchedEffect(startDate) { - val adjustedStartDate = when { - startDate.isBefore(tomorrow) -> tomorrow - startDate.isAfter(maxDate) -> maxDate - else -> startDate + // 날짜가 조정되었으면 업데이트 + var needsUpdate = false + if (validatedStart != startDate) { + startDate = validatedStart + needsUpdate = true } - - if (adjustedStartDate != startDate) { - startDate = adjustedStartDate + if (validatedEnd != endDate) { + endDate = validatedEnd + needsUpdate = true } - // 끝 날짜가 시작 날짜보다 빠르면 조정 - if (endDate.isBefore(startDate.plusDays(1))) { - endDate = startDate.plusDays(1) + // 유효한 범위이고 조정이 없었으면 콜백 호출 + if (!needsUpdate && validatedEnd.isAfter(validatedStart)) { + onDateRangeSelected(validatedStart, validatedEnd) } } - LaunchedEffect(endDate) { - val adjustedEndDate = when { - endDate.isAfter(maxDate) -> maxDate - endDate.isBefore(startDate.plusDays(1)) -> startDate.plusDays(1) - else -> endDate - } - - if (adjustedEndDate != endDate) { - endDate = adjustedEndDate - } - } + // 날짜 범위 계산 + val daysBetween = ChronoUnit.DAYS.between(startDate, endDate) + val isOverLimit = daysBetween > 91 Column(modifier = modifier.fillMaxWidth()) { Text( @@ -111,6 +101,7 @@ fun GroupRoomDurationPicker( minDate = tomorrow, maxDate = maxDate, onDateSelected = { newDate -> + isPickerTouched = true startDate = newDate }, modifier = Modifier @@ -135,6 +126,7 @@ fun GroupRoomDurationPicker( minDate = tomorrow, maxDate = maxDate, onDateSelected = { newDate -> + isPickerTouched = true endDate = newDate }, modifier = Modifier diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt index 9a355d52..b93b4a74 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt @@ -1,5 +1,6 @@ package com.texthip.thip.ui.group.makeroom.util +import java.time.LocalDate import kotlin.math.roundToInt object WheelPickerUtils { @@ -27,4 +28,29 @@ object WheelPickerUtils { return if (circular) getCircularIndex(centerIndex, size) else centerIndex.coerceIn(0, size - 1) } + + @JvmStatic + fun validateDateRange( + startDate: LocalDate, + endDate: LocalDate, + minDate: LocalDate, + maxDate: LocalDate + ): Pair { + // 1. 시작 날짜 유효성 검사 + val validatedStart = when { + startDate.isBefore(minDate) -> minDate + startDate.isAfter(maxDate) -> maxDate + else -> startDate + } + + // 2. 종료 날짜 유효성 검사 (최소 1일 기간 보장) + val minEndDate = validatedStart.plusDays(1) + val validatedEnd = when { + endDate.isAfter(maxDate) -> maxDate + endDate.isBefore(minEndDate) -> minEndDate + else -> endDate + } + + return validatedStart to validatedEnd + } } From aee09b59a42491f73a7b19b623ef3bb74cb64213 Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Mon, 27 Oct 2025 18:16:17 +0900 Subject: [PATCH 08/22] =?UTF-8?q?[refactor]:=20=EB=AA=A8=EC=9E=84=20?= =?UTF-8?q?=ED=99=9C=EB=8F=99=EA=B8=B0=EA=B0=84=20picker=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=20=EC=88=98=EC=A0=95=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../makeroom/component/GroupDatePicker.kt | 6 ++-- .../component/GroupRoomDurationPicker.kt | 28 +++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt index dae96fa1..c7ce6060 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt @@ -62,7 +62,8 @@ fun GroupDatePicker( val newDate = try { LocalDate.of(newYear, month, day) } catch (e: Exception) { - LocalDate.of(newYear, month, 1) + val lastDay = LocalDate.of(newYear, month, 1).lengthOfMonth() + LocalDate.of(newYear, month, lastDay) } onDateSelected(newDate) }, @@ -88,7 +89,8 @@ fun GroupDatePicker( val newDate = try { LocalDate.of(year, newMonth, day) } catch (e: Exception) { - LocalDate.of(year, newMonth, 1) + val lastDay = LocalDate.of(year, newMonth, 1).lengthOfMonth() + LocalDate.of(year, newMonth, lastDay) } onDateSelected(newDate) }, diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt index 0bb07ed2..54d6b5df 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt @@ -27,6 +27,7 @@ import com.texthip.thip.ui.theme.ThipTheme.colors import com.texthip.thip.ui.theme.ThipTheme.typography import java.time.LocalDate import java.time.temporal.ChronoUnit +import kotlinx.coroutines.delay @Composable fun GroupRoomDurationPicker( @@ -41,6 +42,7 @@ fun GroupRoomDurationPicker( var startDate by rememberSaveable { mutableStateOf(tomorrow) } var endDate by rememberSaveable { mutableStateOf(tomorrow.plusDays(1)) } var isPickerTouched by rememberSaveable { mutableStateOf(false) } + var debouncedRecruitmentDays by rememberSaveable { mutableStateOf(null) } // 첫 시작 시에만 모든 날짜를 내일 기준으로 초기화 LaunchedEffect(Unit) { @@ -48,6 +50,18 @@ fun GroupRoomDurationPicker( startDate = tomorrow endDate = tomorrow.plusDays(1) isInitialized = true + // picker 업데이트 완료 대기 후 상태 리셋 + delay(100) + isPickerTouched = false + debouncedRecruitmentDays = null + } + } + + // 모집 기간 계산 (디바운싱은 1초로 했습니다) + LaunchedEffect(startDate, isPickerTouched) { + if (isPickerTouched) { + delay(1000) + debouncedRecruitmentDays = ChronoUnit.DAYS.between(today, startDate).toInt() } } @@ -143,6 +157,8 @@ fun GroupRoomDurationPicker( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End ) { + val recruitmentDays = debouncedRecruitmentDays + when { isOverLimit -> { Text( @@ -162,6 +178,18 @@ fun GroupRoomDurationPicker( modifier = Modifier.padding(top = 12.dp) ) } + isPickerTouched && recruitmentDays != null -> { + Text( + text = stringResource( + R.string.group_room_duration_recruitment_period, + recruitmentDays + ), + style = typography.info_r400_s12, + color = colors.NeonGreen, + textAlign = TextAlign.End, + modifier = Modifier.padding(top = 12.dp) + ) + } else -> { Text( text = stringResource( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e23ee58a..72de40c0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -304,6 +304,7 @@ %1$d월 %2$d일 자정에 자동으로 모집 마감되고 활동이 가능합니다. 검색해서 찾기 모임방 활동이 시작되면, 독서메이트 모집이 자동으로 종료돼요. + 활동 시작 전 독서메이트 모집 기간 : %d일 %1$d / %2$d From b0c15057d6919c2d4415e4d26ad2b48cd7aac053 Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Mon, 27 Oct 2025 18:25:29 +0900 Subject: [PATCH 09/22] =?UTF-8?q?[refactor]:=20picker=20=EB=B6=80=EB=B6=84?= =?UTF-8?q?=20=EA=B8=B0=ED=83=80=20=EC=88=98=EC=A0=95=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt | 2 +- .../thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt index c7ce6060..6ed4a0e9 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt @@ -111,7 +111,7 @@ fun GroupDatePicker( GroupWheelPicker( modifier = Modifier.width(32.dp), items = days, - selectedItem = day.coerceAtMost(days.max()), + selectedItem = day.coerceAtMost(days.maxOrNull() ?: 1), onItemSelected = { newDay -> val newDate = LocalDate.of(year, month, newDay) onDateSelected(newDate) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt index 54d6b5df..a5c1f953 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt @@ -137,7 +137,7 @@ fun GroupRoomDurationPicker( // 끝 날짜 Picker GroupDatePicker( selectedDate = endDate, - minDate = tomorrow, + minDate = startDate.plusDays(1), maxDate = maxDate, onDateSelected = { newDate -> isPickerTouched = true From 1bedd40381688633fc9a71c324b0cc45eb8ecaf2 Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Tue, 28 Oct 2025 12:36:06 +0900 Subject: [PATCH 10/22] =?UTF-8?q?[refactor]:=20DialogPopup=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../texthip/thip/ui/common/modal/DialogPopup.kt | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt b/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt index 37229e1d..601ae6cb 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt @@ -4,9 +4,11 @@ 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.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -24,43 +26,46 @@ fun DialogPopup( modifier: Modifier = Modifier, title: String, description: String, + confirmText: String = stringResource(R.string.yes), + cancelText: String = stringResource(R.string.no), onConfirm: () -> Unit, onCancel: () -> Unit ) { Column( modifier = modifier - .size(width = 320.dp, height = 182.dp) + .width(320.dp) .background( color = colors.DarkGrey, shape = RoundedCornerShape(12.dp) ) - .padding(20.dp), - verticalArrangement = Arrangement.SpaceBetween, + .padding(20.dp) ) { Text( text = title, color = colors.White, style = typography.smalltitle_m500_s18_h24, ) + Spacer(modifier = Modifier.height(12.dp)) Text( text = description, color = colors.White, style = typography.copy_r400_s14, ) + Spacer(modifier = Modifier.height(20.dp)) Row( horizontalArrangement = Arrangement.spacedBy(20.dp), modifier = Modifier.fillMaxWidth() ) { ActionMediumButton( - text = stringResource(R.string.no), + text = cancelText, contentColor = colors.White, backgroundColor = colors.Grey02, modifier = Modifier.weight(1f), onClick = onCancel, ) ActionMediumButton( - text = stringResource(R.string.yes), + text = confirmText, contentColor = colors.White, backgroundColor = colors.Purple, modifier = Modifier.weight(1f), From 39e96f486a36df4be164da54543bbe98feb1c2d8 Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Tue, 28 Oct 2025 12:47:28 +0900 Subject: [PATCH 11/22] =?UTF-8?q?[refactor]:=20stringResource=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/values/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 72de40c0..1085d124 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,6 +31,8 @@ 내 피드에 추가 아니오 + 취소 + 확인 글 등록 팔로우 책 변경 @@ -306,6 +308,8 @@ 모임방 활동이 시작되면, 독서메이트 모집이 자동으로 종료돼요. 활동 시작 전 독서메이트 모집 기간 : %d일 %1$d / %2$d + 모임 만들기 + - 모집기간 : %1$s ~ %2$s\n- 활동기간 : %3$s ~ %4$s\n\n모집기간은 독서메이트 신청을 받는 기간, 활동기간은 실제 모임이 진행되는 기간이에요.\n독서메이트 모집은 조기 마감이 가능하며, 즉시 활동을 시작할 수 있어요.\n\n이 모임을 생성할까요? 띱! From 01f35dba6ce0833a43eee2df69146819cf672869 Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Tue, 28 Oct 2025 12:48:39 +0900 Subject: [PATCH 12/22] =?UTF-8?q?[refactor]:=20viewModel=EC=97=90=20dialog?= =?UTF-8?q?=20=EB=B3=B4=EC=97=AC=EC=A3=BC=EB=8A=94=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/texthip/thip/ui/common/modal/DialogPopup.kt | 2 ++ .../ui/group/makeroom/viewmodel/GroupMakeRoomUiState.kt | 3 ++- .../ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt | 7 +++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt b/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt index 601ae6cb..68699969 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.texthip.thip.R import com.texthip.thip.ui.common.buttons.ActionMediumButton import com.texthip.thip.ui.theme.ThipTheme.colors @@ -50,6 +51,7 @@ fun DialogPopup( text = description, color = colors.White, style = typography.copy_r400_s14, + lineHeight = 22.sp ) Spacer(modifier = Modifier.height(20.dp)) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomUiState.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomUiState.kt index 212b4416..f4147470 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomUiState.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomUiState.kt @@ -29,7 +29,8 @@ data class GroupMakeRoomUiState( val isLastSearchPage: Boolean = false, val currentSearchQuery: String = "", val genres: List = emptyList(), - val isBookPreselected: Boolean = false + val isBookPreselected: Boolean = false, + val showConfirmDialog: Boolean = false ) { // 유효성 검사 로직 val isDurationValid: Boolean diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt index 554abe9a..73a9febb 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt @@ -3,11 +3,10 @@ package com.texthip.thip.ui.group.makeroom.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.texthip.thip.R +import com.texthip.thip.data.manager.Genre import com.texthip.thip.data.model.book.response.BookSavedResponse import com.texthip.thip.data.model.book.response.BookSearchItem -import com.texthip.thip.data.model.book.response.BookUserSaveList import com.texthip.thip.data.model.rooms.request.CreateRoomRequest -import com.texthip.thip.data.manager.Genre import com.texthip.thip.data.provider.StringResourceProvider import com.texthip.thip.data.repository.BookRepository import com.texthip.thip.data.repository.RoomsRepository @@ -324,6 +323,10 @@ class GroupMakeRoomViewModel @Inject constructor( updateState { it.copy(password = password) } } + fun toggleConfirmDialog(show: Boolean = true) { + updateState { it.copy(showConfirmDialog = show) } + } + fun createGroup(onSuccess: (Int) -> Unit, onError: (String) -> Unit) { val currentState = _uiState.value From 18530c561ad2f6d1e11c960e24893179111591ea Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Tue, 28 Oct 2025 12:49:11 +0900 Subject: [PATCH 13/22] =?UTF-8?q?[refactor]:=20=EC=99=84=EB=A3=8C=EB=A5=BC?= =?UTF-8?q?=20=EB=88=84=EB=A5=B4=EB=A9=B4=20dialog=EB=A5=BC=20=EB=B3=B4?= =?UTF-8?q?=EC=97=AC=EC=A3=BC=EA=B3=A0=20=ED=99=95=EC=9D=B8=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=EC=97=90=20=EA=B8=B0=EC=A1=B4=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EC=99=84=EB=A3=8C=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../makeroom/screen/GroupMakeRoomScreen.kt | 54 +++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt index 3f63d87d..077fabf1 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt @@ -1,5 +1,6 @@ package com.texthip.thip.ui.group.makeroom.screen +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -12,6 +13,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -20,6 +22,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.blur +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview @@ -30,6 +33,7 @@ import com.texthip.thip.data.manager.Genre import com.texthip.thip.ui.common.buttons.GenreChipRow import com.texthip.thip.ui.common.buttons.ToggleSwitchButton import com.texthip.thip.ui.common.forms.WarningTextField +import com.texthip.thip.ui.common.modal.DialogPopup import com.texthip.thip.ui.common.topappbar.InputTopAppBar import com.texthip.thip.ui.group.makeroom.component.GroupBookSearchBottomSheet import com.texthip.thip.ui.group.makeroom.component.GroupInputField @@ -45,7 +49,9 @@ import com.texthip.thip.ui.theme.ThipTheme.colors import com.texthip.thip.ui.theme.ThipTheme.typography import com.texthip.thip.utils.rooms.advancedImePadding import com.texthip.thip.utils.rooms.toDisplayStrings +import java.time.format.DateTimeFormatter +private val DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy.MM.dd") @Composable fun GroupMakeRoomScreen( @@ -66,7 +72,7 @@ fun GroupMakeRoomScreen( GroupMakeRoomContent( uiState = uiState, onNavigateBack = onNavigateBack, - onCreateGroup = { + onCreateGroup = { viewModel.createGroup( onSuccess = { roomId -> onGroupCreated(roomId) @@ -74,6 +80,7 @@ fun GroupMakeRoomScreen( onError = { } ) }, + onToggleConfirmDialog = viewModel::toggleConfirmDialog, onSelectBook = viewModel::selectBook, onToggleBookSearchSheet = viewModel::toggleBookSearchSheet, onSelectGenre = viewModel::selectGenre, @@ -96,6 +103,7 @@ fun GroupMakeRoomContent( uiState: GroupMakeRoomUiState, onNavigateBack: () -> Unit = {}, onCreateGroup: () -> Unit = {}, + onToggleConfirmDialog: (Boolean) -> Unit = {}, onSelectBook: (BookData) -> Unit = {}, onToggleBookSearchSheet: (Boolean) -> Unit = {}, onSelectGenre: (Int) -> Unit = {}, @@ -115,7 +123,7 @@ fun GroupMakeRoomContent( Column( modifier = modifier .fillMaxSize() - .then(if (uiState.showBookSearchSheet) Modifier.blur(5.dp) else Modifier), + .then(if (uiState.showBookSearchSheet || uiState.showConfirmDialog || uiState.isLoading) Modifier.blur(5.dp) else Modifier), verticalArrangement = Arrangement.Top, horizontalAlignment = Alignment.CenterHorizontally ) { @@ -123,7 +131,7 @@ fun GroupMakeRoomContent( title = stringResource(R.string.group_making_group), isRightButtonEnabled = uiState.isFormValid && !uiState.isLoading, onLeftClick = onNavigateBack, - onRightClick = onCreateGroup + onRightClick = { onToggleConfirmDialog(true) } ) Column( @@ -155,7 +163,7 @@ fun GroupMakeRoomContent( genres = uiState.genres.toDisplayStrings(), selectedIndex = uiState.selectedGenreIndex, onSelect = onSelectGenre, - horizontalArrangement = Arrangement.Start + horizontalArrangement = Arrangement.Center ) Spacer(modifier = Modifier.height(12.dp)) @@ -275,6 +283,44 @@ fun GroupMakeRoomContent( showGroupBooksTab = false ) } + + if (uiState.showConfirmDialog) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + DialogPopup( + title = stringResource(R.string.group_create_confirm_title), + description = stringResource( + R.string.group_create_confirm_message, + java.time.LocalDate.now().format(DATE_FORMATTER), + uiState.meetingStartDate.format(DATE_FORMATTER), + uiState.meetingStartDate.format(DATE_FORMATTER), + uiState.meetingEndDate.format(DATE_FORMATTER) + ), + confirmText = stringResource(R.string.confirm), + cancelText = stringResource(R.string.cancel), + onConfirm = { + onToggleConfirmDialog(false) + onCreateGroup() + }, + onCancel = { + onToggleConfirmDialog(false) + } + ) + } + } + + if (uiState.isLoading) { + Box( + modifier = Modifier + .fillMaxSize() + .background(Color.Black.copy(alpha = 0.5f)), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator(color = colors.NeonGreen) + } + } } } From a52cb8d85903337a19971d80dc8d1de3cb84566d Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Tue, 28 Oct 2025 14:09:18 +0900 Subject: [PATCH 14/22] =?UTF-8?q?[refactor]:=20padding=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt b/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt index 68699969..315930c2 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt @@ -46,7 +46,7 @@ fun DialogPopup( color = colors.White, style = typography.smalltitle_m500_s18_h24, ) - Spacer(modifier = Modifier.height(12.dp)) + Spacer(modifier = Modifier.height(20.dp)) Text( text = description, color = colors.White, From 2ed3600c9bdece83078d762a91a768c494028377 Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Tue, 28 Oct 2025 14:15:42 +0900 Subject: [PATCH 15/22] =?UTF-8?q?[chore]:=20=EC=A3=BC=EC=84=9D=20=EB=B0=8F?= =?UTF-8?q?=20=EC=A4=84=20=EB=B0=94=EA=BF=88=20=EC=88=98=EC=A0=95=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/GroupBookListWithScrollbar.kt | 4 +-- .../component/GroupBookSearchBottomSheet.kt | 2 ++ .../makeroom/component/GroupDatePicker.kt | 8 +++--- .../component/GroupRoomDurationPicker.kt | 3 +++ .../makeroom/component/GroupWheelPicker.kt | 26 ++++++++++++------- .../makeroom/screen/GroupMakeRoomScreen.kt | 6 ++++- .../makeroom/util/WheelPickerDisplayUtils.kt | 4 +-- .../viewmodel/GroupMakeRoomViewModel.kt | 26 ++++++++++--------- 8 files changed, 48 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookListWithScrollbar.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookListWithScrollbar.kt index d24206e3..654ef20f 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookListWithScrollbar.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookListWithScrollbar.kt @@ -36,7 +36,7 @@ fun GroupBookListWithScrollbar( onLoadMore: () -> Unit = {} ) { val listState = rememberLazyListState() - + val shouldLoadMore = remember { derivedStateOf { val layoutInfo = listState.layoutInfo @@ -76,7 +76,7 @@ fun GroupBookListWithScrollbar( ) Spacer(modifier = Modifier.height(12.dp)) } - + if (isLoadingMore) { item { Box( diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt index 8cf59dee..d2645e38 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupBookSearchBottomSheet.kt @@ -144,6 +144,7 @@ fun GroupBookSearchBottomSheet( onLoadMore = onLoadMoreSearch ) } + !showGroupBooksTab || selectedTab == 0 -> { GroupBookListWithScrollbar( books = displayBooks, @@ -153,6 +154,7 @@ fun GroupBookSearchBottomSheet( onLoadMore = onLoadMoreSaved ) } + else -> { GroupBookListWithScrollbar( books = displayBooks, diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt index 6ed4a0e9..1d4bde99 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt @@ -40,12 +40,12 @@ fun GroupDatePicker( val day = selectedDate.dayOfMonth // 유효한 범위 계산 - 날짜 변경 시 안정성을 위해 remember 사용 - val years = remember(minDate.year, maxDate.year) { - (minDate.year..maxDate.year).toList() + val years = remember(minDate.year, maxDate.year) { + (minDate.year..maxDate.year).toList() } val months = remember { (1..12).toList() } - val days = remember(year, month) { - (1..LocalDate.of(year, month, 1).lengthOfMonth()).toList() + val days = remember(year, month) { + (1..LocalDate.of(year, month, 1).lengthOfMonth()).toList() } Row( diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt index a5c1f953..582c0f3d 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt @@ -169,6 +169,7 @@ fun GroupRoomDurationPicker( modifier = Modifier.padding(top = 12.dp) ) } + !isPickerTouched -> { Text( text = stringResource(R.string.group_room_duration_initial_comment), @@ -178,6 +179,7 @@ fun GroupRoomDurationPicker( modifier = Modifier.padding(top = 12.dp) ) } + isPickerTouched && recruitmentDays != null -> { Text( text = stringResource( @@ -190,6 +192,7 @@ fun GroupRoomDurationPicker( modifier = Modifier.padding(top = 12.dp) ) } + else -> { Text( text = stringResource( diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupWheelPicker.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupWheelPicker.kt index fdf71339..a9d2819c 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupWheelPicker.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupWheelPicker.kt @@ -68,17 +68,23 @@ fun GroupWheelPicker( val spacingPx = remember(density) { with(density) { 9.dp.toPx() } } val itemSpacing = remember(itemHeightPx, spacingPx) { itemHeightPx + spacingPx } - val getCircularIndex = remember(items.size) { { index: Int -> - WheelPickerUtils.getCircularIndex(index, items.size) - } } + val getCircularIndex = remember(items.size) { + { index: Int -> + WheelPickerUtils.getCircularIndex(index, items.size) + } + } - val normalizeOffset = remember(itemSpacing, items.size, circular) { { offset: Float -> - WheelPickerUtils.normalizeOffset(offset, itemSpacing, items.size, circular) - } } + val normalizeOffset = remember(itemSpacing, items.size, circular) { + { offset: Float -> + WheelPickerUtils.normalizeOffset(offset, itemSpacing, items.size, circular) + } + } - val offsetToIndex = remember(itemSpacing, items.size, circular) { { offset: Float -> - WheelPickerUtils.offsetToIndex(offset, itemSpacing, items.size, circular) - } } + val offsetToIndex = remember(itemSpacing, items.size, circular) { + { offset: Float -> + WheelPickerUtils.offsetToIndex(offset, itemSpacing, items.size, circular) + } + } // 선택 아이템이 바뀌면 중앙에 오도록 offset 이동 LaunchedEffect(selectedItem) { @@ -104,7 +110,7 @@ fun GroupWheelPicker( val containerHeight = remember(itemHeight) { (itemHeight * 3 + 36).dp } val textStyle = typography.info_r400_s12 - + Box( modifier = modifier.height(containerHeight) ) { diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt index 077fabf1..e0a59b6b 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt @@ -123,7 +123,11 @@ fun GroupMakeRoomContent( Column( modifier = modifier .fillMaxSize() - .then(if (uiState.showBookSearchSheet || uiState.showConfirmDialog || uiState.isLoading) Modifier.blur(5.dp) else Modifier), + .then( + if (uiState.showBookSearchSheet || uiState.showConfirmDialog || uiState.isLoading) Modifier.blur( + 5.dp + ) else Modifier + ), verticalArrangement = Arrangement.Top, horizontalAlignment = Alignment.CenterHorizontally ) { diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt index b93b4a74..0bbd85f3 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt @@ -36,14 +36,14 @@ object WheelPickerUtils { minDate: LocalDate, maxDate: LocalDate ): Pair { - // 1. 시작 날짜 유효성 검사 + // 시작 날짜 유효성 검사 val validatedStart = when { startDate.isBefore(minDate) -> minDate startDate.isAfter(maxDate) -> maxDate else -> startDate } - // 2. 종료 날짜 유효성 검사 (최소 1일 기간 보장) + // 종료 날짜 유효성 검사 val minEndDate = validatedStart.plusDays(1) val validatedEnd = when { endDate.isAfter(maxDate) -> maxDate diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt index 73a9febb..b4db6fe4 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt @@ -97,7 +97,7 @@ class GroupMakeRoomViewModel @Inject constructor( viewModelScope.launch { try { isLoadingSavedBooks = true - + if (isInitial) { updateState { it.copy(savedBooks = emptyList(), isLastSavedBooks = false) } savedBooksCursor = null @@ -110,7 +110,8 @@ class GroupMakeRoomViewModel @Inject constructor( bookRepository.getBooks("SAVED", cursor) .onSuccess { response -> if (response != null) { - val currentList = if (isInitial) emptyList() else _uiState.value.savedBooks + val currentList = + if (isInitial) emptyList() else _uiState.value.savedBooks val newBooks = response.bookList.map { it.toBookData() } updateState { it.copy( @@ -167,28 +168,28 @@ class GroupMakeRoomViewModel @Inject constructor( loadMoreSearchJob?.cancel() if (query.isBlank()) { - updateState { + updateState { it.copy( - searchResults = emptyList(), + searchResults = emptyList(), isSearching = false, searchPage = 1, isLastSearchPage = false, currentSearchQuery = "" - ) + ) } return } searchJob = viewModelScope.launch { delay(300) // 디바운싱 - updateState { + updateState { it.copy( isSearching = true, searchResults = emptyList(), searchPage = 1, isLastSearchPage = false, currentSearchQuery = query - ) + ) } try { @@ -234,23 +235,24 @@ class GroupMakeRoomViewModel @Inject constructor( fun loadMoreSearchResults() { val currentState = _uiState.value - if (currentState.isLoadingMoreSearchResults || + if (currentState.isLoadingMoreSearchResults || currentState.isSearching || - currentState.isLastSearchPage || + currentState.isLastSearchPage || currentState.searchResults.isEmpty() || - currentState.currentSearchQuery.isBlank()) { + currentState.currentSearchQuery.isBlank() + ) { return } loadMoreSearchJob?.cancel() loadMoreSearchJob = viewModelScope.launch { updateState { it.copy(isLoadingMoreSearchResults = true) } - + try { val nextPage = currentState.searchPage + 1 val result = bookRepository.searchBooks( currentState.currentSearchQuery, - page = nextPage, + page = nextPage, isFinalized = false ) result.onSuccess { response -> From 9d4149317044f3af92acce61cc04e4549d52402b Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Tue, 28 Oct 2025 16:16:51 +0900 Subject: [PATCH 16/22] =?UTF-8?q?[refactor]:=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?=EB=B2=94=EC=9C=84=20=EA=B2=80=EC=A6=9D=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../makeroom/component/GroupDatePicker.kt | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt index 1d4bde99..81d1759d 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt @@ -59,13 +59,19 @@ fun GroupDatePicker( items = years, selectedItem = year, onItemSelected = { newYear -> - val newDate = try { + val fallbackDate = try { LocalDate.of(newYear, month, day) } catch (e: Exception) { val lastDay = LocalDate.of(newYear, month, 1).lengthOfMonth() LocalDate.of(newYear, month, lastDay) } - onDateSelected(newDate) + // 폴백된 날짜가 minDate/maxDate 범위를 벗어나지 않도록 보정 + val validatedDate = when { + fallbackDate.isBefore(minDate) -> minDate + fallbackDate.isAfter(maxDate) -> maxDate + else -> fallbackDate + } + onDateSelected(validatedDate) }, displayText = { it.toString() } ) @@ -86,13 +92,19 @@ fun GroupDatePicker( items = months, selectedItem = month, onItemSelected = { newMonth -> - val newDate = try { + val fallbackDate = try { LocalDate.of(year, newMonth, day) } catch (e: Exception) { val lastDay = LocalDate.of(year, newMonth, 1).lengthOfMonth() LocalDate.of(year, newMonth, lastDay) } - onDateSelected(newDate) + // 폴백된 날짜가 minDate/maxDate 범위를 벗어나지 않도록 보정 + val validatedDate = when { + fallbackDate.isBefore(minDate) -> minDate + fallbackDate.isAfter(maxDate) -> maxDate + else -> fallbackDate + } + onDateSelected(validatedDate) }, displayText = { it.toString() } ) @@ -114,7 +126,13 @@ fun GroupDatePicker( selectedItem = day.coerceAtMost(days.maxOrNull() ?: 1), onItemSelected = { newDay -> val newDate = LocalDate.of(year, month, newDay) - onDateSelected(newDate) + // 날짜가 minDate/maxDate 범위를 벗어나지 않도록 보정 + val validatedDate = when { + newDate.isBefore(minDate) -> minDate + newDate.isAfter(maxDate) -> maxDate + else -> newDate + } + onDateSelected(validatedDate) }, displayText = { it.toString() } ) From c21590979b633ed39972c4bcee4b3aad984c3647 Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Tue, 28 Oct 2025 17:13:44 +0900 Subject: [PATCH 17/22] =?UTF-8?q?[refactor]:=20Dialog=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt index e0a59b6b..9eed363a 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.blur import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource +import androidx.compose.ui.window.Dialog import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -289,9 +290,8 @@ fun GroupMakeRoomContent( } if (uiState.showConfirmDialog) { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center + Dialog( + onDismissRequest = { onToggleConfirmDialog(false) } ) { DialogPopup( title = stringResource(R.string.group_create_confirm_title), From 5c58a6d5e8167d8322918b09610bd81ec7cce439 Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Tue, 28 Oct 2025 17:26:16 +0900 Subject: [PATCH 18/22] =?UTF-8?q?[refactor]:=20pickerUtil=20=EA=B2=BD?= =?UTF-8?q?=EA=B3=97=EA=B0=92=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../makeroom/util/WheelPickerDisplayUtils.kt | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt index 0bbd85f3..0694af72 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt @@ -36,20 +36,13 @@ object WheelPickerUtils { minDate: LocalDate, maxDate: LocalDate ): Pair { - // 시작 날짜 유효성 검사 - val validatedStart = when { - startDate.isBefore(minDate) -> minDate - startDate.isAfter(maxDate) -> maxDate - else -> startDate - } + // 시작 날짜: minDate와 (maxDate - 1일) 사이로 제한 + // (종료일과 최소 1일 간격을 보장하기 위해 maxDate보다 1일 작게 제한) + val validatedStart = startDate.coerceIn(minDate, maxDate.minusDays(1)) - // 종료 날짜 유효성 검사 + // 종료 날짜: (시작일 + 1일)과 maxDate 사이로 제한 val minEndDate = validatedStart.plusDays(1) - val validatedEnd = when { - endDate.isAfter(maxDate) -> maxDate - endDate.isBefore(minEndDate) -> minEndDate - else -> endDate - } + val validatedEnd = endDate.coerceIn(minEndDate, maxDate) return validatedStart to validatedEnd } From a4814ab53b542fd6846e546268c5553e04352f47 Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Wed, 29 Oct 2025 01:33:33 +0900 Subject: [PATCH 19/22] =?UTF-8?q?[refactor]:=20pickerUtil=20=EA=B2=BD?= =?UTF-8?q?=EA=B3=97=EA=B0=92=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../makeroom/util/WheelPickerDisplayUtils.kt | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt b/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt index 0694af72..0bbd85f3 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/makeroom/util/WheelPickerDisplayUtils.kt @@ -36,13 +36,20 @@ object WheelPickerUtils { minDate: LocalDate, maxDate: LocalDate ): Pair { - // 시작 날짜: minDate와 (maxDate - 1일) 사이로 제한 - // (종료일과 최소 1일 간격을 보장하기 위해 maxDate보다 1일 작게 제한) - val validatedStart = startDate.coerceIn(minDate, maxDate.minusDays(1)) + // 시작 날짜 유효성 검사 + val validatedStart = when { + startDate.isBefore(minDate) -> minDate + startDate.isAfter(maxDate) -> maxDate + else -> startDate + } - // 종료 날짜: (시작일 + 1일)과 maxDate 사이로 제한 + // 종료 날짜 유효성 검사 val minEndDate = validatedStart.plusDays(1) - val validatedEnd = endDate.coerceIn(minEndDate, maxDate) + val validatedEnd = when { + endDate.isAfter(maxDate) -> maxDate + endDate.isBefore(minEndDate) -> minEndDate + else -> endDate + } return validatedStart to validatedEnd } From 75f23b08e1646f48c2cd85b97517c42841a307a0 Mon Sep 17 00:00:00 2001 From: rbqks529 Date: Tue, 11 Nov 2025 17:07:38 +0900 Subject: [PATCH 20/22] =?UTF-8?q?[refactor]:=20viewModel=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EC=88=98=EC=A0=95=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/inspectionProfiles/Project_Default.xml | 1 + .../component/GroupBookListWithScrollbar.kt | 2 +- .../viewmodel/GroupMakeRoomViewModel.kt | 18 ++++++++++-------- .../permission/NotificationPermissionUtils.kt | 6 +++--- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 7061a0d6..82d900ed 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -57,5 +57,6 @@