From 7b03b0fde14f3625e898a9291e562c9c01170d7e Mon Sep 17 00:00:00 2001 From: chanmi Date: Wed, 17 Jun 2026 02:12:47 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[feat]=20=EC=BB=AC=EB=A0=89=EC=85=98=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20AddContentScreen.kt=20=ED=95=98=EB=8B=A8?= =?UTF-8?q?=20=EA=B7=B8=EB=9D=BC=EB=8D=B0=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../collectioncreate/AddContentScreen.kt | 190 ++++++++++-------- 1 file changed, 103 insertions(+), 87 deletions(-) diff --git a/app/src/main/java/com/flint/presentation/collectioncreate/AddContentScreen.kt b/app/src/main/java/com/flint/presentation/collectioncreate/AddContentScreen.kt index d6bde25d..a6be4692 100644 --- a/app/src/main/java/com/flint/presentation/collectioncreate/AddContentScreen.kt +++ b/app/src/main/java/com/flint/presentation/collectioncreate/AddContentScreen.kt @@ -2,6 +2,7 @@ package com.flint.presentation.collectioncreate import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer @@ -22,7 +23,10 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.tooling.preview.Preview @@ -95,109 +99,121 @@ fun AddContentScreen( } } - Column( + Box( modifier = modifier - .fillMaxWidth() + .fillMaxSize() .background(color = FlintTheme.colors.background), ) { - FlintBackTopAppbar( - onClick = onBackClick, - title = "작품 추가하기", - actionText = "추가", - onActionClick = { - if (selectedContents.isNotEmpty()) { - onActionClick() - } - }, - textStyle = if (selectedContents.isNotEmpty()) FlintTheme.typography.body1M16 else FlintTheme.typography.body1Sb16, - textColor = if (selectedContents.isNotEmpty()) FlintTheme.colors.secondary400 else FlintTheme.colors.gray300, - ) + Column(modifier = Modifier.fillMaxSize()) { + FlintBackTopAppbar( + onClick = onBackClick, + title = "작품 추가하기", + actionText = "추가", + onActionClick = { + if (selectedContents.isNotEmpty()) { + onActionClick() + } + }, + textStyle = if (selectedContents.isNotEmpty()) FlintTheme.typography.body1M16 else FlintTheme.typography.body1Sb16, + textColor = if (selectedContents.isNotEmpty()) FlintTheme.colors.secondary400 else FlintTheme.colors.gray300, + ) - Spacer(modifier = Modifier.height(12.dp)) - - FlintSearchTextField( - placeholder = "추천하고 싶은 작품을 검색해보세요", - value = uiState.searchText, - onValueChanged = onSearchTextChanged, - onSearchAction = {}, - onClearAction = {}, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search), - keyboardActions = KeyboardActions( - onSearch = { keyboardController?.hide() } - ), - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) + Spacer(modifier = Modifier.height(12.dp)) - Spacer(modifier = Modifier.height(16.dp)) + FlintSearchTextField( + placeholder = "추천하고 싶은 작품을 검색해보세요", + value = uiState.searchText, + onValueChanged = onSearchTextChanged, + onSearchAction = {}, + onClearAction = {}, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search), + keyboardActions = KeyboardActions( + onSearch = { keyboardController?.hide() } + ), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) - if (selectedContents.isNotEmpty()) { - LazyRow( - state = lazyRowState, - contentPadding = PaddingValues(horizontal = 16.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp), - reverseLayout = true, - ) { - items( - items = selectedContents, - key = { it.id }, - ) { content -> - SelectedContentItem( - imageUrl = content.posterUrl, - onRemoveClick = { - if (uiState.isCancelModalVisible) { - contentToDelete = content - isModalVisible = true - } else { - onRemoveContent(content) - } - }, - ) - } - } Spacer(modifier = Modifier.height(16.dp)) - } - if (contentList.isEmpty() && uiState.searchText.isNotBlank()){ - FlintSearchEmptyView( - title = "아직 준비 중인 작품이에요", - modifier = Modifier.fillMaxSize() - ) - } - else{ - LazyColumn( - modifier = Modifier.fillMaxSize(), - contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), - verticalArrangement = Arrangement.spacedBy(16.dp), - ) { - items( - items = contentList, - key = { it.id }, - ) { content -> - val isSelected = selectedContents.any { it.id == content.id } - - AddContentSelectItem( - onCheckClick = { - if (isSelected){ + if (selectedContents.isNotEmpty()) { + LazyRow( + state = lazyRowState, + contentPadding = PaddingValues(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + reverseLayout = true, + ) { + items( + items = selectedContents, + key = { it.id }, + ) { content -> + SelectedContentItem( + imageUrl = content.posterUrl, + onRemoveClick = { if (uiState.isCancelModalVisible) { contentToDelete = content isModalVisible = true } else { - onToggleContent(content) + onRemoveContent(content) } - } else onToggleContent(content) - }, - isSelected = isSelected, - posterImageUrl = content.posterUrl, - title = content.title, - director = content.author, - createdYear = content.year, - ) + }, + ) + } + } + Spacer(modifier = Modifier.height(16.dp)) + } + + if (contentList.isEmpty() && uiState.searchText.isNotBlank()) { + FlintSearchEmptyView( + title = "아직 준비 중인 작품이에요", + modifier = Modifier.fillMaxSize() + ) + } else { + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + items( + items = contentList, + key = { it.id }, + ) { content -> + val isSelected = selectedContents.any { it.id == content.id } + + AddContentSelectItem( + onCheckClick = { + if (isSelected) { + if (uiState.isCancelModalVisible) { + contentToDelete = content + isModalVisible = true + } else { + onToggleContent(content) + } + } else onToggleContent(content) + }, + isSelected = isSelected, + posterImageUrl = content.posterUrl, + title = content.title, + director = content.author, + createdYear = content.year, + ) + } } } } - + + Box( + modifier = Modifier + .fillMaxWidth() + .height(148.dp) + .align(Alignment.BottomCenter) + .background( + Brush.verticalGradient( + colors = listOf(Color.Transparent, FlintTheme.colors.background) + ) + ) + ) } if (isModalVisible) { CollectionCreateContentDeleteModal( From 951684a5556c349e1e79611bda8c54e11343ad9d Mon Sep 17 00:00:00 2001 From: chanmi Date: Wed, 17 Jun 2026 02:19:33 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[refactor]=20=EC=BB=AC=EB=A0=89=EC=85=98=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20'=EC=9D=B4=20=EC=9E=91=ED=92=88=EC=9D=84?= =?UTF-8?q?=20=EC=84=A0=ED=83=9D=ED=95=9C=20=EC=9D=B4=EC=9C=A0'=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=ED=99=9C=EC=84=B1=ED=99=94=20=ED=95=84?= =?UTF-8?q?=EC=88=98=20=ED=95=AD=EB=AA=A9=EC=9C=BC=EB=A1=9C=20=EB=A7=8C?= =?UTF-8?q?=EB=93=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../collectioncreate/uistate/CollectionCreateUiState.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/flint/presentation/collectioncreate/uistate/CollectionCreateUiState.kt b/app/src/main/java/com/flint/presentation/collectioncreate/uistate/CollectionCreateUiState.kt index de80c34b..71ac0fc3 100644 --- a/app/src/main/java/com/flint/presentation/collectioncreate/uistate/CollectionCreateUiState.kt +++ b/app/src/main/java/com/flint/presentation/collectioncreate/uistate/CollectionCreateUiState.kt @@ -22,7 +22,8 @@ data class CollectionCreateUiState( !isLoading && title.isNotBlank() && isPublic != null && - selectedContents.size >= 2 + selectedContents.size >= 2 && + selectedContents.all { contentDetailsMap[it.id]?.reason?.isNotBlank() == true } val isCancelModalVisible: Boolean = contentDetailsMap.values.any { it.reason.isNotBlank() } From 822c5f4ba0b2e8d5aa62a2349b0de8a57d473eb4 Mon Sep 17 00:00:00 2001 From: chanmi Date: Wed, 17 Jun 2026 23:37:02 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[refactor]=EC=B6=A9=EB=8F=8C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../uistate/CollectionCreateUiState.kt | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/flint/presentation/collectioncreate/uistate/CollectionCreateUiState.kt b/app/src/main/java/com/flint/presentation/collectioncreate/uistate/CollectionCreateUiState.kt index 71ac0fc3..67bb6fbb 100644 --- a/app/src/main/java/com/flint/presentation/collectioncreate/uistate/CollectionCreateUiState.kt +++ b/app/src/main/java/com/flint/presentation/collectioncreate/uistate/CollectionCreateUiState.kt @@ -9,6 +9,7 @@ import kotlinx.collections.immutable.persistentListOf @Immutable data class CollectionCreateUiState( val thumbnailImageUri: Uri? = null, + val existingThumbnailUrl: String? = null, val title: String = "", val description: String = "", val isPublic: Boolean? = null, @@ -17,12 +18,39 @@ data class CollectionCreateUiState( val contents: ImmutableList = persistentListOf(), val searchText: String = "", val isLoading: Boolean = false, + // 수정 모드 원본값 (null이면 생성 모드) + val originalTitle: String? = null, + val originalDescription: String = "", + val originalIsPublic: Boolean? = null, + val originalThumbnailUrl: String? = null, + val originalContentIds: Set = emptySet(), + val originalContentDetails: Map> = emptyMap(), + val originalContentImageUrls: Map> = emptyMap(), ) { - val isFinishButtonEnabled: Boolean = + private val isEditMode: Boolean get() = originalTitle != null + + private val hasChanges: Boolean get() = isEditMode && ( + title != originalTitle || + description != originalDescription || + isPublic != originalIsPublic || + thumbnailImageUri != null || + existingThumbnailUrl != originalThumbnailUrl || + selectedContents.map { it.id }.toSet() != originalContentIds || + contentDetailsMap.any { (id, detail) -> + val original = originalContentDetails[id] + detail.isSpoiler != original?.first || + detail.reason != original?.second || + detail.contentImageUris.isNotEmpty() || + detail.existingImageUrls != (originalContentImageUrls[id] ?: emptyList()) + } + ) + + val isFinishButtonEnabled: Boolean get() = !isLoading && title.isNotBlank() && isPublic != null && selectedContents.size >= 2 && + (!isEditMode || hasChanges) && selectedContents.all { contentDetailsMap[it.id]?.reason?.isNotBlank() == true } val isCancelModalVisible: Boolean = @@ -33,5 +61,6 @@ data class CollectionCreateUiState( data class ContentDetail( val isSpoiler: Boolean = false, val reason: String = "", + val existingImageUrls: List = emptyList(), val contentImageUris: List = emptyList(), -) \ No newline at end of file +)