From 401ed4c3cdc5ac55ead2b61efaf1f7bbb011b41b Mon Sep 17 00:00:00 2001 From: ckals413 Date: Thu, 18 Jun 2026 16:43:24 +0900 Subject: [PATCH 01/10] =?UTF-8?q?fix:=20=EB=B6=81=EB=A7=88=ED=81=AC=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD=20=EC=8B=9C=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=EB=90=9C=20=EC=BD=98=ED=85=90=EC=B8=A0=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=EA=B0=9C=EC=88=98=20=EB=8F=99=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/flint/presentation/profile/SavedContentViewModel.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt b/app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt index 721730d5..172d9191 100644 --- a/app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt +++ b/app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt @@ -87,6 +87,7 @@ class SavedContentViewModel @Inject constructor( val currentList = (state.contents as? UiState.Success)?.data ?: return@update state val updated = currentList.copy( + totalCount = currentList.totalCount + if (isBookmarked) 1 else -1, contents = currentList.contents .map { if (it.id == contentId) it.copy(isBookmarked = isBookmarked) else it } .toPersistentList() From ee07e5ff1c78c9bf8eda4c014e7016fc498750ae Mon Sep 17 00:00:00 2001 From: ckals413 Date: Thu, 18 Jun 2026 16:59:10 +0900 Subject: [PATCH 02/10] =?UTF-8?q?feat:=20=EC=9E=91=ED=92=88=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EC=B5=9C=EC=86=8C=20=EA=B0=9C=EC=88=98=20=EC=A0=9C?= =?UTF-8?q?=ED=95=9C=20=EB=AA=A8=EB=8B=AC=20=EC=B6=94=EA=B0=80=20=EB=B0=8F?= =?UTF-8?q?=20=EB=B6=81=EB=A7=88=ED=81=AC=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CollectionDetailScreen.kt | 18 +++++++ .../CollectionDetailViewModel.kt | 50 +++++++++++++++---- .../sideeffect/CollectionDetailSideEffect.kt | 2 + 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt b/app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt index d6428046..92398b66 100644 --- a/app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt +++ b/app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt @@ -41,6 +41,8 @@ import com.flint.domain.model.collection.CollectionDetailModelNew import com.flint.domain.model.content.ContentModelNew import com.flint.domain.type.UserRoleType import com.flint.presentation.collectiondetail.component.CollectionCopyrightFooter +import com.flint.R +import com.flint.core.designsystem.component.modal.OneButtonModal import com.flint.presentation.collectiondetail.component.CollectionDetailDeleteModal import com.flint.presentation.collectiondetail.component.CollectionDetailDescription import com.flint.presentation.collectiondetail.component.CollectionDetailThumbnail @@ -74,6 +76,7 @@ fun CollectionDetailRoute( var showContentSaveToast: Boolean by remember { mutableStateOf(false) } var showContentCancelToast: Boolean by remember { mutableStateOf(false) } var showDeleteModal: Boolean by remember { mutableStateOf(false) } + var showBookmarkRestrictionModal: Boolean by remember { mutableStateOf(false) } var showEditSuccessToastState: Boolean by remember { mutableStateOf(showEditSuccessToast) } when (val uiState = uiState) { @@ -123,6 +126,17 @@ fun CollectionDetailRoute( else -> {} } + if (showBookmarkRestrictionModal) { + OneButtonModal( + title = "작품 저장을 취소할 수 없어요", + message = "취향 키워드 분석을 위해\n최소 5개의 작품을 저장해주세요", + buttonText = "확인", + onConfirm = { showBookmarkRestrictionModal = false }, + onDismiss = { showBookmarkRestrictionModal = false }, + icon = R.drawable.ic_gradient_bookmark, + ) + } + if (showDeleteModal) { CollectionDetailDeleteModal( onConfirm = { @@ -218,6 +232,10 @@ fun CollectionDetailRoute( } } + CollectionDetailSideEffect.ToggleContentBookmarkMinLimitExceeded -> { + showBookmarkRestrictionModal = true + } + CollectionDetailSideEffect.DeleteCollectionSuccess -> navigateUpWithDeleteSuccess() CollectionDetailSideEffect.DeleteCollectionFailure -> { diff --git a/app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailViewModel.kt b/app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailViewModel.kt index f7bed4f2..feb5fb66 100644 --- a/app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailViewModel.kt +++ b/app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailViewModel.kt @@ -11,6 +11,7 @@ import com.flint.data.local.PreferencesManager import com.flint.domain.model.bookmark.CollectionBookmarkUsersModel import com.flint.domain.model.collection.CollectionDetailModelNew import com.flint.domain.model.content.ContentModelNew +import com.flint.domain.model.bookmark.BookmarkException import com.flint.domain.repository.BookmarkRepository import com.flint.domain.repository.CollectionRepository import com.flint.presentation.collectiondetail.sideeffect.CollectionDetailSideEffect @@ -107,14 +108,47 @@ class CollectionDetailViewModel @Inject constructor( val newBookmarkState: Boolean = !targetContent.isBookmarked val initialBookmarkCount: Int = targetContent.bookmarkCount - val adjustedBookmarkCount: Int = - if (newBookmarkState) initialBookmarkCount + 1 - else (initialBookmarkCount - 1).coerceAtLeast(0) + // 북마크 취소 + if (!newBookmarkState) { + contentBookmarkDebounceJobs[contentId]?.cancel() + contentBookmarkDebounceJobs.remove(contentId) + val initialState = initialContentBookmarkStates.remove(contentId) + + if (initialState == false) { + updateContentBookmarkState( + contentId = contentId, + isBookmarked = false, + bookmarkCount = (initialBookmarkCount - 1).coerceAtLeast(0), + ) + return + } + + viewModelScope.launch { + bookmarkRepository.toggleContentBookmark(contentId) + .onSuccess { isBookmarked: Boolean -> + updateContentBookmarkState( + contentId = contentId, + isBookmarked = isBookmarked, + bookmarkCount = (initialBookmarkCount - 1).coerceAtLeast(0), + ) + _sideEffect.emit(CollectionDetailSideEffect.ToggleContentBookmarkSuccess(isBookmarked)) + } + .onFailure { throwable -> + if (throwable is BookmarkException.ContentMinLimitExceeded) { + _sideEffect.emit(CollectionDetailSideEffect.ToggleContentBookmarkMinLimitExceeded) + } + } + } + return + } + + // 북마크 추가 + val adjustedBookmarkCount: Int = initialBookmarkCount + 1 updateContentBookmarkState( contentId = contentId, isBookmarked = newBookmarkState, - bookmarkCount = adjustedBookmarkCount + bookmarkCount = adjustedBookmarkCount, ) contentBookmarkDebounceJobs[contentId]?.cancel() @@ -132,7 +166,7 @@ class CollectionDetailViewModel @Inject constructor( .onSuccess { isBookmarked: Boolean -> updateContentIsBookmarkedOnly( contentId = contentId, - isBookmarked = isBookmarked + isBookmarked = isBookmarked, ) _sideEffect.emit( CollectionDetailSideEffect.ToggleContentBookmarkSuccess(isBookmarked) @@ -143,14 +177,10 @@ class CollectionDetailViewModel @Inject constructor( (_uiState.value as? UiState.Success)?.data?.collectionDetail?.contents ?.find { it.id == contentId } ?: return@onFailure - val rollbackCount: Int = - if (initialState) fallbackContent.bookmarkCount + 1 - else (fallbackContent.bookmarkCount - 1).coerceAtLeast(0) - updateContentBookmarkState( contentId = contentId, isBookmarked = initialState, - bookmarkCount = rollbackCount + bookmarkCount = (fallbackContent.bookmarkCount - 1).coerceAtLeast(0), ) } } diff --git a/app/src/main/java/com/flint/presentation/collectiondetail/sideeffect/CollectionDetailSideEffect.kt b/app/src/main/java/com/flint/presentation/collectiondetail/sideeffect/CollectionDetailSideEffect.kt index 0bfedc1e..8b196305 100644 --- a/app/src/main/java/com/flint/presentation/collectiondetail/sideeffect/CollectionDetailSideEffect.kt +++ b/app/src/main/java/com/flint/presentation/collectiondetail/sideeffect/CollectionDetailSideEffect.kt @@ -7,6 +7,8 @@ sealed interface CollectionDetailSideEffect { class ToggleContentBookmarkSuccess(val isBookmarked: Boolean) : CollectionDetailSideEffect + object ToggleContentBookmarkMinLimitExceeded : CollectionDetailSideEffect + object DeleteCollectionSuccess : CollectionDetailSideEffect object DeleteCollectionFailure : CollectionDetailSideEffect From 76592a91da8eee68ce247bfef16176ccfb5598ba Mon Sep 17 00:00:00 2001 From: ckals413 Date: Thu, 18 Jun 2026 17:49:44 +0900 Subject: [PATCH 03/10] =?UTF-8?q?feat:=20=EC=A0=80=EC=9E=A5=EB=90=9C=20?= =?UTF-8?q?=EC=BD=98=ED=85=90=EC=B8=A0=20=ED=81=B4=EB=A6=AD=20=EC=8B=9C=20?= =?UTF-8?q?OTT=20=EB=AA=A9=EB=A1=9D=20=EB=B0=94=ED=85=80=EC=8B=9C=ED=8A=B8?= =?UTF-8?q?=20=ED=91=9C=EC=8B=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../listView/SavedContentsSection.kt | 2 +- .../presentation/profile/ProfileScreen.kt | 7 +++++- .../profile/SavedContentScreen.kt | 24 ++++++++++++++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/flint/core/designsystem/component/listView/SavedContentsSection.kt b/app/src/main/java/com/flint/core/designsystem/component/listView/SavedContentsSection.kt index 0bc59898..ec5f97c4 100644 --- a/app/src/main/java/com/flint/core/designsystem/component/listView/SavedContentsSection.kt +++ b/app/src/main/java/com/flint/core/designsystem/component/listView/SavedContentsSection.kt @@ -27,6 +27,7 @@ import com.flint.core.designsystem.component.listItem.SavedContentItem import com.flint.core.designsystem.theme.FlintTheme import com.flint.domain.model.content.BookmarkedContentListModel import com.flint.domain.model.content.ContentModel +import com.flint.domain.type.OttType import kotlinx.collections.immutable.ImmutableList @Composable @@ -96,7 +97,6 @@ fun SavedContentsSection( SavedContentItem( contentModel = item, onItemClick = { contentId -> - onItemClick(contentId) }, ) diff --git a/app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt b/app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt index 50e30391..1cf9c19e 100644 --- a/app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt +++ b/app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt @@ -48,6 +48,7 @@ import com.flint.core.navigation.model.CollectionListRouteType import com.flint.domain.model.collection.CollectionListModel import com.flint.domain.model.content.BookmarkedContentListModel import com.flint.domain.model.ott.OttListModel +import com.flint.domain.model.ott.OttModel import com.flint.domain.model.user.KeywordListModel import com.flint.domain.model.user.UserProfileResponseModel import com.flint.presentation.MainActivity @@ -104,7 +105,11 @@ fun ProfileRoute( onCollectionItemClick = navigateToCollectionDetail, onSettingsClick = navigateToSetting, onContentItemClick = { contentId -> - viewModel.getOttListPerContent(contentId) + val ottList = (uiState.sectionData as? UiState.Success) + ?.data?.savedContents?.contents + ?.find { it.id == contentId }?.getOttSimpleList ?: emptyList() + ottListModel = OttListModel(otts = ottList.map { OttModel(name = it.name) }) + if (ottListModel.otts.isNotEmpty()) showOttListBottomSheet = true }, onContentMoreClick = { navigateToSavedContentList(uiState.userId) }, onCreatedCollectionMoreClick = { diff --git a/app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt b/app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt index 74f78722..35a86313 100644 --- a/app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt +++ b/app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt @@ -12,9 +12,13 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.ExperimentalMaterial3Api 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.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalSoftwareKeyboardController @@ -25,6 +29,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.flint.R import com.flint.core.common.util.UiState +import com.flint.core.designsystem.component.bottomsheet.OttListBottomSheet import com.flint.core.designsystem.component.indicator.FlintLoadingIndicator import com.flint.core.designsystem.component.modal.OneButtonModal import com.flint.core.designsystem.component.textfield.FlintSearchTextField @@ -33,6 +38,8 @@ import com.flint.core.designsystem.component.view.FlintSearchEmptyView import com.flint.core.designsystem.theme.FlintTheme import com.flint.domain.model.content.BookmarkedContentItemModel import com.flint.domain.model.content.BookmarkedContentListModel +import com.flint.domain.model.ott.OttListModel +import com.flint.domain.model.ott.OttModel import com.flint.domain.type.OttType import com.flint.presentation.profile.component.CollectionCreateContentBookmark import com.flint.presentation.profile.uistate.SavedContentUiState @@ -58,6 +65,7 @@ fun SavedContentRoute( ) } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun SavedContentScreen( uiState: SavedContentUiState, @@ -69,6 +77,8 @@ fun SavedContentScreen( modifier: Modifier = Modifier, ) { val keyboardController = LocalSoftwareKeyboardController.current + var showOttBottomSheet by remember { mutableStateOf(false) } + var selectedOttList by remember { mutableStateOf>(emptyList()) } Column( modifier = modifier @@ -134,6 +144,10 @@ fun SavedContentScreen( SavedContentList( contents = uiState.filteredContents, onBookmarkClick = onBookmarkClick, + onMoreClick = { ottList -> + selectedOttList = ottList + showOttBottomSheet = true + }, ) } } @@ -151,6 +165,13 @@ fun SavedContentScreen( } } + if (showOttBottomSheet && selectedOttList.isNotEmpty()) { + OttListBottomSheet( + ottList = OttListModel(otts = selectedOttList.map { OttModel(name = it.name) }), + onDismiss = { showOttBottomSheet = false }, + ) + } + // 저장 취소 제한 안내 모달 (저장 작품이 5개일 때 북마크 토글 시 노출) if (uiState.showBookmarkRestrictionModal) { OneButtonModal( @@ -168,6 +189,7 @@ fun SavedContentScreen( private fun SavedContentList( contents: ImmutableList, onBookmarkClick: (contentId: String) -> Unit, + onMoreClick: (ottList: List) -> Unit, modifier: Modifier = Modifier, ) { if (contents.isEmpty()) { @@ -192,7 +214,7 @@ private fun SavedContentList( CollectionCreateContentBookmark( modifier = Modifier.animateItem(), onBookmarkClick = { onBookmarkClick(content.id) }, - onMoreClick = {}, + onMoreClick = { onMoreClick(content.getOttSimpleList) }, isBookmarked = content.isBookmarked, bookmarkCount = content.bookmarkCount, imageUrl = content.imageUrl, From fb4e2c8e9a66b1291a70e88fbbfc276ec2ece6f5 Mon Sep 17 00:00:00 2001 From: ckals413 Date: Thu, 18 Jun 2026 17:53:39 +0900 Subject: [PATCH 04/10] =?UTF-8?q?feat:=20=EC=A0=80=EC=9E=A5=20=EC=9E=91?= =?UTF-8?q?=ED=92=88=20=ED=99=94=EB=A9=B4=20=EB=82=B4=20=EB=B6=81=EB=A7=88?= =?UTF-8?q?=ED=81=AC=20=ED=86=A0=EC=8A=A4=ED=8A=B8=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../profile/SavedContentScreen.kt | 40 +++++++++++++++++++ .../profile/SavedContentSideEffect.kt | 5 +++ .../profile/SavedContentViewModel.kt | 7 ++++ 3 files changed, 52 insertions(+) create mode 100644 app/src/main/java/com/flint/presentation/profile/SavedContentSideEffect.kt diff --git a/app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt b/app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt index 35a86313..3028e7b0 100644 --- a/app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt +++ b/app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -32,6 +33,7 @@ import com.flint.core.common.util.UiState import com.flint.core.designsystem.component.bottomsheet.OttListBottomSheet import com.flint.core.designsystem.component.indicator.FlintLoadingIndicator import com.flint.core.designsystem.component.modal.OneButtonModal +import com.flint.core.designsystem.component.toast.ShowToast import com.flint.core.designsystem.component.textfield.FlintSearchTextField import com.flint.core.designsystem.component.topappbar.FlintBackTopAppbar import com.flint.core.designsystem.component.view.FlintSearchEmptyView @@ -53,6 +55,24 @@ fun SavedContentRoute( viewModel: SavedContentViewModel = hiltViewModel(), ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() + var showSaveToast by remember { mutableStateOf(false) } + var showCancelToast by remember { mutableStateOf(false) } + + LaunchedEffect(Unit) { + viewModel.sideEffect.collect { effect -> + when (effect) { + is SavedContentSideEffect.ToggleBookmarkSuccess -> { + if (effect.isBookmarked) { + showSaveToast = true + showCancelToast = false + } else { + showCancelToast = true + showSaveToast = false + } + } + } + } + } SavedContentScreen( uiState = uiState, @@ -63,6 +83,26 @@ fun SavedContentRoute( onDismissRestrictionModal = viewModel::dismissBookmarkRestrictionModal, modifier = Modifier.padding(paddingValues), ) + + if (showSaveToast) { + ShowToast( + text = "작품을 저장했어요", + imageVector = null, + paddingValues = paddingValues, + yOffset = 12.dp, + hide = { showSaveToast = false }, + ) + } + + if (showCancelToast) { + ShowToast( + text = "작품 저장이 취소되었어요", + imageVector = null, + paddingValues = paddingValues, + yOffset = 12.dp, + hide = { showCancelToast = false }, + ) + } } @OptIn(ExperimentalMaterial3Api::class) diff --git a/app/src/main/java/com/flint/presentation/profile/SavedContentSideEffect.kt b/app/src/main/java/com/flint/presentation/profile/SavedContentSideEffect.kt new file mode 100644 index 00000000..aa62a8aa --- /dev/null +++ b/app/src/main/java/com/flint/presentation/profile/SavedContentSideEffect.kt @@ -0,0 +1,5 @@ +package com.flint.presentation.profile + +sealed interface SavedContentSideEffect { + data class ToggleBookmarkSuccess(val isBookmarked: Boolean) : SavedContentSideEffect +} diff --git a/app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt b/app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt index 172d9191..60bbfebf 100644 --- a/app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt +++ b/app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt @@ -12,8 +12,11 @@ import com.flint.domain.repository.UserRepository import com.flint.presentation.profile.uistate.SavedContentUiState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch @@ -32,6 +35,9 @@ class SavedContentViewModel @Inject constructor( private val _uiState = MutableStateFlow(SavedContentUiState()) val uiState: StateFlow = _uiState.asStateFlow() + private val _sideEffect = MutableSharedFlow() + val sideEffect: SharedFlow = _sideEffect.asSharedFlow() + init { loadBookmarkedContents() } @@ -94,6 +100,7 @@ class SavedContentViewModel @Inject constructor( ) state.copy(contents = UiState.Success(updated)) } + _sideEffect.emit(SavedContentSideEffect.ToggleBookmarkSuccess(isBookmarked)) } .onFailure { throwable -> if (throwable is BookmarkException.ContentMinLimitExceeded) { From 6abb457d6ddd8e3d1a850d218040bcc46ba7a6e6 Mon Sep 17 00:00:00 2001 From: ckals413 Date: Thu, 18 Jun 2026 18:07:59 +0900 Subject: [PATCH 05/10] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=ED=9B=84=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EA=B0=B1?= =?UTF-8?q?=EC=8B=A0=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/flint/presentation/main/MainNavHost.kt | 11 +++++++++++ .../flint/presentation/profile/ProfileScreen.kt | 9 +++++++++ .../flint/presentation/profile/ProfileViewModel.kt | 11 +++++++++++ .../profile/navigation/ProfileNavigation.kt | 14 +++++++++++++- .../setting/editprofile/EditProfileScreen.kt | 6 +++++- .../navigation/EditProfileNavigation.kt | 2 ++ 6 files changed, 51 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/flint/presentation/main/MainNavHost.kt b/app/src/main/java/com/flint/presentation/main/MainNavHost.kt index eb8099ea..413be350 100644 --- a/app/src/main/java/com/flint/presentation/main/MainNavHost.kt +++ b/app/src/main/java/com/flint/presentation/main/MainNavHost.kt @@ -22,6 +22,8 @@ import com.flint.presentation.explore.navigation.exploreNavGraph import com.flint.presentation.home.navigation.homeNavGraph import com.flint.presentation.login.navigation.loginNavGraph import com.flint.presentation.onboarding.navigation.onBoardingNavGraph +import com.flint.core.navigation.MainTabRoute +import com.flint.presentation.profile.navigation.KEY_PROFILE_UPDATED import com.flint.presentation.profile.navigation.myProfileNavGraph import com.flint.presentation.profile.navigation.profileNavGraph import com.flint.presentation.savedcontent.navigation.savedContentListNavGraph @@ -144,6 +146,15 @@ fun MainNavHost( editProfileNavGraph( navigateUp = navigator::navigateUp, + onProfileSaved = { + try { + navigator.navController + .getBackStackEntry(MainTabRoute.Profile) + .savedStateHandle[KEY_PROFILE_UPDATED] = true + } catch (_: IllegalArgumentException) { + // MainTabRoute.Profile이 백스택에 없는 경우 무시 + } + }, ) withdrawNavGraph( diff --git a/app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt b/app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt index 1cf9c19e..2983cad6 100644 --- a/app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt +++ b/app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt @@ -67,6 +67,8 @@ fun ProfileRoute( navigateToSavedContentList: (userId: String?) -> Unit, navigateToCollectionDetail: (collectionId: String) -> Unit, navigateToSetting: () -> Unit = {}, + shouldRefreshProfile: Boolean = false, + onProfileRefreshed: () -> Unit = {}, viewModel: ProfileViewModel = hiltViewModel(), ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -77,6 +79,13 @@ fun ProfileRoute( var ottListModel by remember { mutableStateOf(OttListModel()) } val sheetState = rememberModalBottomSheetState() + LaunchedEffect(shouldRefreshProfile) { + if (shouldRefreshProfile) { + viewModel.reloadUserProfile() + onProfileRefreshed() + } + } + LaunchedEffect(Unit) { viewModel.sideEffect.collect { sideEffect -> when (sideEffect) { diff --git a/app/src/main/java/com/flint/presentation/profile/ProfileViewModel.kt b/app/src/main/java/com/flint/presentation/profile/ProfileViewModel.kt index ba6cc961..799f3be2 100644 --- a/app/src/main/java/com/flint/presentation/profile/ProfileViewModel.kt +++ b/app/src/main/java/com/flint/presentation/profile/ProfileViewModel.kt @@ -149,6 +149,17 @@ class ProfileViewModel @Inject constructor( } } + // 프로필 헤더만 재조회 (섹션 데이터는 유지) + fun reloadUserProfile() { + viewModelScope.launch { + userRepository.getUserProfile(userId = userId) + .onSuccess { profile -> + _uiState.update { it.copy(profile = profile) } + } + .onFailure { Timber.e(it) } + } + } + fun recalculateKeywords() = viewModelScope.launch { _uiState.update { it.copy(isRecalculating = true) } userRepository.recalculateKeywords() diff --git a/app/src/main/java/com/flint/presentation/profile/navigation/ProfileNavigation.kt b/app/src/main/java/com/flint/presentation/profile/navigation/ProfileNavigation.kt index a3b4eee4..1612f1b4 100644 --- a/app/src/main/java/com/flint/presentation/profile/navigation/ProfileNavigation.kt +++ b/app/src/main/java/com/flint/presentation/profile/navigation/ProfileNavigation.kt @@ -1,6 +1,8 @@ package com.flint.presentation.profile.navigation import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.runtime.getValue +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions @@ -10,6 +12,8 @@ import com.flint.core.navigation.Route import com.flint.core.navigation.model.CollectionListRouteType import com.flint.presentation.profile.ProfileRoute +const val KEY_PROFILE_UPDATED = "key_profile_updated" + fun NavController.navigateToMyProfile( navOptions: NavOptions? = null ) { @@ -30,7 +34,11 @@ fun NavGraphBuilder.myProfileNavGraph( navigateToCollectionDetail: (collectionId: String) -> Unit, navigateToSetting: () -> Unit = {}, ) { - composable { + composable { entry -> + val shouldRefreshProfile by entry.savedStateHandle + .getStateFlow(KEY_PROFILE_UPDATED, false) + .collectAsStateWithLifecycle() + ProfileRoute( paddingValues = paddingValues, navigateUp = {}, @@ -38,6 +46,10 @@ fun NavGraphBuilder.myProfileNavGraph( navigateToSavedContentList = navigateToSavedContentList, navigateToCollectionDetail = navigateToCollectionDetail, navigateToSetting = navigateToSetting, + shouldRefreshProfile = shouldRefreshProfile, + onProfileRefreshed = { + entry.savedStateHandle[KEY_PROFILE_UPDATED] = false + }, ) } } diff --git a/app/src/main/java/com/flint/presentation/setting/editprofile/EditProfileScreen.kt b/app/src/main/java/com/flint/presentation/setting/editprofile/EditProfileScreen.kt index c1a31628..5a0ccd1e 100644 --- a/app/src/main/java/com/flint/presentation/setting/editprofile/EditProfileScreen.kt +++ b/app/src/main/java/com/flint/presentation/setting/editprofile/EditProfileScreen.kt @@ -54,6 +54,7 @@ import com.flint.core.designsystem.theme.FlintTheme @Composable fun EditProfileRoute( navigateUp: () -> Unit, + onProfileSaved: () -> Unit = {}, viewModel: EditProfileViewModel = hiltViewModel(), ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -63,7 +64,10 @@ fun EditProfileRoute( } LaunchedEffect(Unit) { - viewModel.navigateUp.collect { navigateUp() } + viewModel.navigateUp.collect { + onProfileSaved() + navigateUp() + } } EditProfileScreen( diff --git a/app/src/main/java/com/flint/presentation/setting/editprofile/navigation/EditProfileNavigation.kt b/app/src/main/java/com/flint/presentation/setting/editprofile/navigation/EditProfileNavigation.kt index 99ff0e84..a8cad6fb 100644 --- a/app/src/main/java/com/flint/presentation/setting/editprofile/navigation/EditProfileNavigation.kt +++ b/app/src/main/java/com/flint/presentation/setting/editprofile/navigation/EditProfileNavigation.kt @@ -13,10 +13,12 @@ fun NavController.navigateToEditProfile(navOptions: NavOptions? = null) { fun NavGraphBuilder.editProfileNavGraph( navigateUp: () -> Unit, + onProfileSaved: () -> Unit = {}, ) { composable { EditProfileRoute( navigateUp = navigateUp, + onProfileSaved = onProfileSaved, ) } } From 40b395e45d50b610cfe46729dd06b150cb398365 Mon Sep 17 00:00:00 2001 From: ckals413 Date: Sat, 20 Jun 2026 11:47:40 +0900 Subject: [PATCH 06/10] =?UTF-8?q?feat:=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/flint/presentation/profile/SavedContentViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt b/app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt index 60bbfebf..6f3549fe 100644 --- a/app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt +++ b/app/src/main/java/com/flint/presentation/profile/SavedContentViewModel.kt @@ -93,7 +93,7 @@ class SavedContentViewModel @Inject constructor( val currentList = (state.contents as? UiState.Success)?.data ?: return@update state val updated = currentList.copy( - totalCount = currentList.totalCount + if (isBookmarked) 1 else -1, + totalCount = maxOf(0, currentList.totalCount + if (isBookmarked) 1 else -1), contents = currentList.contents .map { if (it.id == contentId) it.copy(isBookmarked = isBookmarked) else it } .toPersistentList() From 0266617d26f18c1628848dacdef380d5047f9441 Mon Sep 17 00:00:00 2001 From: ckals413 Date: Sat, 20 Jun 2026 12:47:20 +0900 Subject: [PATCH 07/10] =?UTF-8?q?feat:=20=ED=83=80=EC=9D=B4=ED=8B=80=20?= =?UTF-8?q?=EC=98=81=EC=97=AD=20=EC=9E=90=EB=8F=99=20=EC=A4=84=20=EB=B0=94?= =?UTF-8?q?=EA=BF=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../onboarding/OnboardingContentScreen.kt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/flint/presentation/onboarding/OnboardingContentScreen.kt b/app/src/main/java/com/flint/presentation/onboarding/OnboardingContentScreen.kt index 3b2f97d9..becf496b 100644 --- a/app/src/main/java/com/flint/presentation/onboarding/OnboardingContentScreen.kt +++ b/app/src/main/java/com/flint/presentation/onboarding/OnboardingContentScreen.kt @@ -141,10 +141,21 @@ fun OnboardingContentScreen( // 타이틀 영역 - 스크롤됨 item(span = { GridItemSpan(3) }) { Column { + var useMultiLine by remember(nickname) { mutableStateOf(false) } Text( - text = "${nickname}님이 좋아하는 작품\n7개를 골라주세요", + text = if (useMultiLine) { + "${nickname}님이\n좋아하는 작품\n7개를 골라주세요" + } else { + "${nickname}님이 좋아하는 작품\n7개를 골라주세요" + }, color = FlintTheme.colors.white, style = FlintTheme.typography.display2M28, + onTextLayout = { result -> + // 2줄 포맷인데 실제 렌더링이 3줄 이상이면 자연 줄바꿈 발생한 것 + if (!useMultiLine && result.lineCount > 2) { + useMultiLine = true + } + }, ) Spacer(modifier = Modifier.height(8.dp)) From 757c850e8e632b828debcd4c9c007cd51cec7459 Mon Sep 17 00:00:00 2001 From: ckals413 Date: Sat, 20 Jun 2026 13:15:05 +0900 Subject: [PATCH 08/10] =?UTF-8?q?feat:=20=EC=84=A0=ED=83=9D=EB=90=9C=20?= =?UTF-8?q?=EC=BD=98=ED=85=90=EC=B8=A0=20=EC=95=84=EC=9D=B4=ED=85=9C=20?= =?UTF-8?q?=ED=81=B4=EB=A6=AD=20=ED=9A=A8=EA=B3=BC=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?=EB=B0=8F=20=EC=98=A8=EB=B3=B4=EB=94=A9=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../designsystem/component/image/SelectedContentItem.kt | 4 ++-- .../presentation/onboarding/OnboardingContentScreen.kt | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/flint/core/designsystem/component/image/SelectedContentItem.kt b/app/src/main/java/com/flint/core/designsystem/component/image/SelectedContentItem.kt index 899e6e74..fd929695 100644 --- a/app/src/main/java/com/flint/core/designsystem/component/image/SelectedContentItem.kt +++ b/app/src/main/java/com/flint/core/designsystem/component/image/SelectedContentItem.kt @@ -1,7 +1,7 @@ package com.flint.core.designsystem.component.image import androidx.compose.foundation.background -import androidx.compose.foundation.clickable +import com.flint.core.common.extension.noRippleClickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape @@ -43,7 +43,7 @@ fun SelectedContentItem( Modifier .align(Alignment.TopEnd) .size(48.dp) - .clickable { onRemoveClick() }, + .noRippleClickable { onRemoveClick() }, ) } } diff --git a/app/src/main/java/com/flint/presentation/onboarding/OnboardingContentScreen.kt b/app/src/main/java/com/flint/presentation/onboarding/OnboardingContentScreen.kt index becf496b..0685dbbc 100644 --- a/app/src/main/java/com/flint/presentation/onboarding/OnboardingContentScreen.kt +++ b/app/src/main/java/com/flint/presentation/onboarding/OnboardingContentScreen.kt @@ -158,7 +158,7 @@ fun OnboardingContentScreen( }, ) - Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(24.dp)) } } @@ -204,6 +204,8 @@ fun OnboardingContentScreen( } } + Spacer(modifier = Modifier.height(8.dp)) + FlintSearchTextField( placeholder = "작품 이름", value = contentUiState.searchKeyword, @@ -307,7 +309,7 @@ fun OnboardingContentScreen( contentAlignment = Alignment.Center, ) { FlintSearchEmptyView( - title = "작품을 찾을 수 없어요" + title = "아직 준비 중인 작품이에요" ) } } From 57785febf47a6c3d9e68b7a123b3ebf59d93651c Mon Sep 17 00:00:00 2001 From: ckals413 Date: Sat, 20 Jun 2026 23:20:53 +0900 Subject: [PATCH 09/10] =?UTF-8?q?feat:=20=EC=A0=80=EC=9E=A5=EB=90=9C=20?= =?UTF-8?q?=EC=9E=91=ED=92=88=20=EA=B2=80=EC=83=89=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EA=B0=9C=EC=88=98=20=ED=91=9C=EC=8B=9C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/flint/presentation/profile/SavedContentScreen.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt b/app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt index 3028e7b0..d2f65bb5 100644 --- a/app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt +++ b/app/src/main/java/com/flint/presentation/profile/SavedContentScreen.kt @@ -146,12 +146,12 @@ fun SavedContentScreen( onClearAction = onClearSearch, ) - // "총 n개"는 실제 데이터가 있는 Success 상태에서만 노출 - if (uiState.contents is UiState.Success) { + // "총 n개"는 검색 결과가 있을 때만 노출 + if (uiState.contents is UiState.Success && uiState.filteredContents.isNotEmpty()) { Spacer(modifier = Modifier.height(16.dp)) Text( - text = "총 ${uiState.totalCount}개", + text = "총 ${uiState.filteredContents.size}개", modifier = Modifier.padding(horizontal = 16.dp), color = FlintTheme.colors.gray100, style = FlintTheme.typography.body2R14, From 077b94e561e50bd9199548052fbc3651bc3c882c Mon Sep 17 00:00:00 2001 From: ckals413 Date: Sat, 20 Jun 2026 23:30:05 +0900 Subject: [PATCH 10/10] =?UTF-8?q?fix:=20=EC=98=A4=ED=83=88=EC=9E=90=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flint/presentation/onboarding/OnboardingContentScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/flint/presentation/onboarding/OnboardingContentScreen.kt b/app/src/main/java/com/flint/presentation/onboarding/OnboardingContentScreen.kt index 0685dbbc..3bac8412 100644 --- a/app/src/main/java/com/flint/presentation/onboarding/OnboardingContentScreen.kt +++ b/app/src/main/java/com/flint/presentation/onboarding/OnboardingContentScreen.kt @@ -290,7 +290,7 @@ fun OnboardingContentScreen( contentAlignment = Alignment.Center, ) { FlintSearchEmptyView( - title = "아직 준비 중인 작품이이요" + title = "아직 준비 중인 작품이에요" ) } }