From d720f51749811f412a5c7a2cc86ab6061da74c45 Mon Sep 17 00:00:00 2001 From: yrsel Date: Mon, 23 Feb 2026 16:13:00 +0900 Subject: [PATCH 01/26] =?UTF-8?q?feat:=20=EB=B6=81=EB=A7=88=ED=81=AC=20?= =?UTF-8?q?=EC=BD=98=ED=85=90=EC=B8=A0=20=EB=AA=A8=EB=91=90=20=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=20=ED=99=94=EB=A9=B4=20UI=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 3 + .../ui/bookmarks/BookmarkContentActivity.kt | 49 +++ .../bookmarks/BookmarkContentScreen.kt | 315 ++++++++++++++++++ .../bookmarks/BookmarkContentUiEffect.kt | 7 + .../bookmarks/BookmarkContentUiState.kt | 10 +- .../bookmarks/BookmarkContentViewModel.kt | 128 +++++++ .../component/BookmarkContentAppBar.kt | 49 +++ .../component/BookmarkContentItem.kt | 292 ++++++++++++++++ .../mypage/component/BookmarkedContentItem.kt | 2 +- .../component/BookmarkedContentSection.kt | 2 +- .../main/bookmarks/BookmarkContentFragment.kt | 137 +------- .../main/bookmarks/BookmarkContentUiEffect.kt | 19 -- .../bookmarks/BookmarkContentViewModel.kt | 150 --------- .../com/on/turip/ui/mypage/MyPageActivity.kt | 7 +- android/app/src/main/res/values/strings.xml | 2 + 15 files changed, 860 insertions(+), 312 deletions(-) create mode 100644 android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt create mode 100644 android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt create mode 100644 android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiEffect.kt rename android/app/src/main/java/com/on/turip/ui/{main => compose}/bookmarks/BookmarkContentUiState.kt (61%) create mode 100644 android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt create mode 100644 android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentAppBar.kt create mode 100644 android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentItem.kt delete mode 100644 android/app/src/main/java/com/on/turip/ui/main/bookmarks/BookmarkContentUiEffect.kt delete mode 100644 android/app/src/main/java/com/on/turip/ui/main/bookmarks/BookmarkContentViewModel.kt diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 4a8e59719..a31db4508 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -17,6 +17,9 @@ android:theme="@style/Theme.Turip" android:usesCleartextTraffic="true" tools:targetApi="31"> + diff --git a/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt b/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt new file mode 100644 index 000000000..eea4a2e15 --- /dev/null +++ b/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt @@ -0,0 +1,49 @@ +package com.on.turip.ui.bookmarks + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import com.on.turip.ui.compose.bookmarks.BookmarkContentScreen +import com.on.turip.ui.compose.designsystem.theme.TuripTheme +import com.on.turip.ui.login.LoginActivity +import com.on.turip.ui.trip.TripDetailActivity +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class BookmarkContentActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + + setContent { + TuripTheme { + BookmarkContentScreen( + navigateToBack = { + finish() + }, + navigateToLogin = { + val intent: Intent = + LoginActivity.newIntent(this).apply { + flags = + Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + startActivity(intent) + finish() + }, + navigateToContent = { contentId: Long -> + val intent: Intent = + TripDetailActivity.newIntent(context = this, contentId = contentId) + startActivity(intent) + }, + ) + } + } + } + + companion object { + fun newIntent(context: Context): Intent = Intent(context, BookmarkContentActivity::class.java) + } +} diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt new file mode 100644 index 000000000..86454e446 --- /dev/null +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt @@ -0,0 +1,315 @@ +package com.on.turip.ui.compose.bookmarks + +import androidx.compose.foundation.Image +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 +import androidx.compose.foundation.layout.fillMaxSize +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.systemBarsPadding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalResources +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.on.turip.R +import com.on.turip.domain.bookmark.BookmarkContent +import com.on.turip.domain.content.Content +import com.on.turip.domain.content.video.VideoData +import com.on.turip.domain.creator.Creator +import com.on.turip.domain.region.City +import com.on.turip.domain.trip.TripDuration +import com.on.turip.ui.common.error.ErrorUiState +import com.on.turip.ui.common.extensions.showSnackbarWithAction +import com.on.turip.ui.compose.bookmarks.component.BookmarkContentAppBar +import com.on.turip.ui.compose.bookmarks.component.BookmarkContentItem +import com.on.turip.ui.compose.designsystem.component.ErrorScreen +import com.on.turip.ui.compose.designsystem.theme.TuripTheme +import kotlinx.collections.immutable.persistentListOf + +@Composable +fun BookmarkContentScreen( + navigateToBack: () -> Unit, + navigateToLogin: () -> Unit, + navigateToContent: (contentId: Long) -> Unit, + viewModel: BookmarkContentViewModel = hiltViewModel(), +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + val snackbarHostState = remember { SnackbarHostState() } + val resources = LocalResources.current + + LaunchedEffect(Unit) { + viewModel.uiEffect.collect { uiEffect: BookmarkContentUiEffect -> + when (uiEffect) { + BookmarkContentUiEffect.NavigateToLogin -> { + navigateToLogin() + } + + BookmarkContentUiEffect.ShowBookmarkRemoveFailed -> { + snackbarHostState.showSnackbarWithAction( + message = resources.getString(R.string.my_page_snackbar_bookmark_remove_failed), + actionLabel = resources.getString(R.string.my_page_snackbar_bookmark_remove_failed_action), + onAction = viewModel::loadBookmarkContents, + ) + } + } + } + } + + Scaffold( + topBar = { BookmarkContentAppBar(onBackClick = navigateToBack) }, + modifier = + Modifier + .fillMaxSize() + .background(TuripTheme.colors.white) + .systemBarsPadding(), + ) { innerPadding -> + Column( + modifier = Modifier.padding(innerPadding), + ) { + BookmarkContentContent( + uiState = uiState, + onRetryClick = viewModel::loadBookmarkContents, + onContentClick = navigateToContent, + onBookmarkClick = viewModel::removeBookmark, + ) + } + } +} + +@Composable +private fun BookmarkContentContent( + uiState: BookmarkContentUiState, + onRetryClick: () -> Unit, + onContentClick: (contentId: Long) -> Unit, + onBookmarkClick: (contentId: Long) -> Unit, +) { + when { + uiState.isLoading -> { + BookmarkLoading() + } + + uiState.errorUiState != ErrorUiState.None -> { + ErrorScreen( + errorUiState = uiState.errorUiState, + onRetryClick = onRetryClick, + modifier = Modifier.fillMaxSize(), + ) + } + + else -> { + if (uiState.isEmpty) { + BookmarkContentEmpty() + } else { + BookmarkContents( + uiState = uiState, + onContentClick = onContentClick, + onBookmarkClick = onBookmarkClick, + ) + } + } + } +} + +@Composable +private fun BookmarkLoading() { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + CircularProgressIndicator( + modifier = Modifier.size(60.dp), + color = TuripTheme.colors.black, + ) + } +} + +@Composable +private fun BookmarkContentEmpty() { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxSize(), + ) { + Spacer(modifier = Modifier.height(TuripTheme.spacing.huge)) + + Image( + painter = painterResource(R.drawable.mascot), + contentDescription = stringResource(R.string.all_mascot_description), + modifier = Modifier.size(70.dp), + ) + + Spacer(modifier = Modifier.height(TuripTheme.spacing.large)) + + Text( + text = stringResource(R.string.my_page_empty_bookmark_content), + style = TuripTheme.typography.title2, + textAlign = TextAlign.Center, + ) + + Spacer(modifier = Modifier.height(TuripTheme.spacing.extraHuge)) + } +} + +@Composable +private fun BookmarkContents( + uiState: BookmarkContentUiState, + onContentClick: (contentId: Long) -> Unit, + onBookmarkClick: (contentId: Long) -> Unit, +) { + Text( + text = + stringResource( + R.string.bookmark_content_count, + uiState.bookmarkContents.size, + ), + textAlign = TextAlign.End, + style = TuripTheme.typography.info2, + color = TuripTheme.colors.gray03, + modifier = + Modifier + .fillMaxWidth() + .padding(vertical = TuripTheme.spacing.medium) + .padding(end = TuripTheme.spacing.large), + ) + + HorizontalDivider( + modifier = Modifier.fillMaxWidth(), + thickness = 5.dp, + color = TuripTheme.colors.gray01, + ) + + LazyColumn( + contentPadding = PaddingValues(TuripTheme.spacing.medium), + verticalArrangement = Arrangement.spacedBy(TuripTheme.spacing.medium), + ) { + itemsIndexed( + items = uiState.bookmarkContents, + key = { _, item -> item.content.id }, + ) { index, content -> + BookmarkContentItem( + content = content, + showDivider = index != uiState.bookmarkContents.lastIndex, + onContentClick = onContentClick, + onRemoveBookmark = onBookmarkClick, + ) + } + } +} + +@Preview(showBackground = true, name = "로딩") +@Composable +private fun BookmarkContentLoadingPreview() { + TuripTheme { + BookmarkContentContent( + uiState = BookmarkContentUiState.Idle, + onRetryClick = {}, + onContentClick = {}, + onBookmarkClick = {}, + ) + } +} + +@Preview(showBackground = true, name = "북마크 콘텐츠 비어 있는 경우") +@Composable +private fun BookmarkContentEmptyPreview() { + TuripTheme { + BookmarkContentContent( + uiState = + BookmarkContentUiState( + isLoading = false, + bookmarkContents = persistentListOf(), + errorUiState = ErrorUiState.None, + ), + onRetryClick = {}, + onContentClick = {}, + onBookmarkClick = {}, + ) + } +} + +@Preview(showBackground = true, name = "에러") +@Composable +private fun BookmarkContentErrorPreview() { + TuripTheme { + BookmarkContentContent( + uiState = + BookmarkContentUiState( + isLoading = false, + bookmarkContents = persistentListOf(), + errorUiState = ErrorUiState.Network, + ), + onRetryClick = {}, + onContentClick = {}, + onBookmarkClick = {}, + ) + } +} + +@Preview(showBackground = true, name = "정상") +@Composable +private fun BookmarkContentErrorSuccess() { + val contents = + persistentListOf( + BookmarkContent( + content = + Content( + 1L, + Creator(1L, "채널명", ""), + VideoData("콘텐츠 제목이 길면 ...으로 표시되는 것을 확인 ㅇㅇㅇ", "thumbnail", "2026-01-12"), + City("대구"), + true, + ), + tripDuration = TripDuration(1, 2), + tripPlaceCount = 2, + ), + BookmarkContent( + content = + Content( + 2L, + Creator(1L, "채널명이 길어지는 경우 채널명이 길어지는 경우 채널명이 길어지는 경우 채널명이 길어지는 경우 ", ""), + VideoData("콘텐츠 제목", "thumbnail", "2025-01-12"), + City("대구"), + true, + ), + tripDuration = TripDuration(0, 1), + tripPlaceCount = 2, + ), + ) + TuripTheme { + BookmarkContentContent( + uiState = + BookmarkContentUiState( + isLoading = false, + bookmarkContents = contents, + errorUiState = ErrorUiState.None, + ), + onRetryClick = {}, + onContentClick = {}, + onBookmarkClick = {}, + ) + } +} diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiEffect.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiEffect.kt new file mode 100644 index 000000000..cbfd3c5b0 --- /dev/null +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiEffect.kt @@ -0,0 +1,7 @@ +package com.on.turip.ui.compose.bookmarks + +sealed interface BookmarkContentUiEffect { + data object NavigateToLogin : BookmarkContentUiEffect + + data object ShowBookmarkRemoveFailed : BookmarkContentUiEffect +} diff --git a/android/app/src/main/java/com/on/turip/ui/main/bookmarks/BookmarkContentUiState.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiState.kt similarity index 61% rename from android/app/src/main/java/com/on/turip/ui/main/bookmarks/BookmarkContentUiState.kt rename to android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiState.kt index bfcc23228..4a4a28b6f 100644 --- a/android/app/src/main/java/com/on/turip/ui/main/bookmarks/BookmarkContentUiState.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiState.kt @@ -1,11 +1,15 @@ -package com.on.turip.ui.main.bookmarks +package com.on.turip.ui.compose.bookmarks +import androidx.compose.runtime.Immutable import com.on.turip.domain.bookmark.BookmarkContent import com.on.turip.ui.common.error.ErrorUiState +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +@Immutable data class BookmarkContentUiState( val isLoading: Boolean, - val bookmarkContents: List, + val bookmarkContents: ImmutableList, val errorUiState: ErrorUiState, ) { val isEmpty: Boolean @@ -15,7 +19,7 @@ data class BookmarkContentUiState( val Idle: BookmarkContentUiState = BookmarkContentUiState( isLoading = true, - bookmarkContents = emptyList(), + bookmarkContents = persistentListOf(), errorUiState = ErrorUiState.None, ) } diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt new file mode 100644 index 000000000..1e93fb250 --- /dev/null +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt @@ -0,0 +1,128 @@ +package com.on.turip.ui.compose.bookmarks + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.on.turip.core.result.ErrorType +import com.on.turip.core.result.onFailure +import com.on.turip.core.result.onSuccess +import com.on.turip.domain.bookmark.PagedBookmarkContents +import com.on.turip.domain.bookmark.repository.BookmarkRepository +import com.on.turip.ui.common.error.ErrorUiState +import com.on.turip.ui.common.error.UiError +import com.on.turip.ui.common.error.toUiError +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import timber.log.Timber + +@HiltViewModel +class BookmarkContentViewModel @Inject constructor( + private val bookmarkRepository: BookmarkRepository, +) : ViewModel() { + private val _uiState: MutableStateFlow = + MutableStateFlow(BookmarkContentUiState.Idle) + val uiState: StateFlow = _uiState.asStateFlow() + + private val _uiEffect: Channel = Channel(Channel.BUFFERED) + val uiEffect: Flow = _uiEffect.receiveAsFlow() + + init { + loadBookmarkContents() + } + + fun loadBookmarkContents() { + viewModelScope.launch { + _uiState.update { it.copy(isLoading = true) } + + bookmarkRepository + .loadBookmarks(10, 0L) + .onSuccess { result: PagedBookmarkContents -> + Timber.d("북마크 화면 조회 성공") + _uiState.update { state -> + state.copy( + isLoading = false, + bookmarkContents = result.bookmarkContents.toImmutableList(), + errorUiState = ErrorUiState.None, + ) + } + }.onFailure { errorType: ErrorType -> + Timber.e("북마크 화면 조회 에러") + val uiError: UiError = errorType.toUiError() + if (uiError is UiError.Global) { + when (uiError) { + UiError.Global.Network -> { + _uiState.update { + it.copy(isLoading = false, errorUiState = ErrorUiState.Network) + } + } + + UiError.Global.Server -> { + _uiState.update { + it.copy(isLoading = false, errorUiState = ErrorUiState.Server) + } + } + + UiError.Global.TokenExpired -> { + _uiState.update { it.copy(isLoading = false) } + _uiEffect.send(BookmarkContentUiEffect.NavigateToLogin) + } + } + } + } + } + } + + private val removeBookmarkMutex = Mutex() + + // 삭제 진행 중인 콘텐츠에 대해 중복 API 호출 방지용 + private val removingIds = mutableSetOf() + + // 낙관적 UI + fun removeBookmark(contentId: Long) { + viewModelScope.launch { + val acquired = removeBookmarkMutex.withLock { removingIds.add(contentId) } + // 이미 삭제 진행 중이라면 반환 + if (!acquired) return@launch + + try { + val removed = + removeBookmarkMutex.withLock { + val contents = _uiState.value.bookmarkContents + + // 이미 UI 제거 완료된 상태 (API 호출 완료) + if (contents.none { it.content.id == contentId }) return@withLock false + + val updated = + contents.filter { it.content.id != contentId }.toImmutableList() + + _uiState.update { state -> + state.copy(bookmarkContents = updated) + } + + true + } + + if (!removed) return@launch + + bookmarkRepository + .deleteBookmark(contentId) + .onFailure { + _uiEffect.send(BookmarkContentUiEffect.ShowBookmarkRemoveFailed) + } + } finally { + // 중복 API 호출 방지 리소스 정리 + removeBookmarkMutex.withLock { removingIds.remove(contentId) } + } + } + } +} diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentAppBar.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentAppBar.kt new file mode 100644 index 000000000..21fca7e16 --- /dev/null +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentAppBar.kt @@ -0,0 +1,49 @@ +package com.on.turip.ui.compose.bookmarks.component + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.on.turip.R +import com.on.turip.ui.compose.designsystem.component.TuripAppBar +import com.on.turip.ui.compose.designsystem.theme.TuripTheme + +@Composable +fun BookmarkContentAppBar( + onBackClick: () -> Unit, + modifier: Modifier = Modifier, +) { + TuripAppBar( + start = { + Icon( + imageVector = Icons.AutoMirrored.Default.ArrowBack, + contentDescription = stringResource(R.string.all_back_description), + modifier = Modifier.clickable(onClick = onBackClick), + ) + }, + center = { + Text( + text = stringResource(R.string.bookmark_content_title), + style = TuripTheme.typography.title1, + ) + }, + modifier = modifier, + ) +} + +@Preview(showBackground = true) +@Composable +private fun BookmarkContentAppBarPreview() { + TuripTheme { + BookmarkContentAppBar( + onBackClick = {}, + modifier = Modifier.fillMaxWidth(), + ) + } +} diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentItem.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentItem.kt new file mode 100644 index 000000000..60c2c978f --- /dev/null +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentItem.kt @@ -0,0 +1,292 @@ +package com.on.turip.ui.compose.bookmarks.component + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +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.wrapContentSize +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil3.compose.AsyncImage +import coil3.request.ImageRequest +import coil3.request.crossfade +import com.on.turip.R +import com.on.turip.domain.bookmark.BookmarkContent +import com.on.turip.domain.content.Content +import com.on.turip.domain.content.video.VideoData +import com.on.turip.domain.creator.Creator +import com.on.turip.domain.region.City +import com.on.turip.domain.trip.TripDuration +import com.on.turip.ui.common.TuripUrlConverter +import com.on.turip.ui.common.mapper.toUiModel +import com.on.turip.ui.compose.designsystem.theme.TuripTheme + +@Composable +fun BookmarkContentItem( + content: BookmarkContent, + showDivider: Boolean, + onContentClick: (contentId: Long) -> Unit, + onRemoveBookmark: (contentId: Long) -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = + modifier + .clip(TuripTheme.shape.container) + .padding(TuripTheme.spacing.extraSmall), + ) { + Column( + modifier = + Modifier + .fillMaxWidth() + .clickable { onContentClick(content.content.id) }, + ) { + ContentThumbnail( + imageUrl = content.content.videoData.url, + modifier = Modifier.fillMaxWidth(), + ) + + Spacer(modifier = Modifier.height(TuripTheme.spacing.medium)) + + TitleAndChipSection( + title = content.content.videoData.title, + city = content.content.city.name, + ) + + Spacer(modifier = Modifier.height(TuripTheme.spacing.small)) + + ContentInformation( + item = content, + onRemoveBookmark = onRemoveBookmark, + ) + } + + if (showDivider) { + HorizontalDivider( + modifier = Modifier.fillMaxWidth(), + thickness = 1.dp, + color = TuripTheme.colors.gray01, + ) + } + } +} + +@Composable +private fun ContentThumbnail( + imageUrl: String, + modifier: Modifier = Modifier, + contentDescription: String? = null, +) { + val shape = TuripTheme.shape.container + val parsedUrl = TuripUrlConverter.convertVideoThumbnailUrl(imageUrl) + + Box( + modifier = + modifier + .fillMaxWidth() + .aspectRatio(16f / 9f) + .clip(shape) + .border( + width = 1.dp, + color = TuripTheme.colors.gray01, + shape = shape, + ), + ) { + AsyncImage( + model = + ImageRequest + .Builder(LocalContext.current) + .data(parsedUrl) + .crossfade(true) + .build(), + contentDescription = contentDescription, + contentScale = ContentScale.Crop, + modifier = Modifier.matchParentSize(), + error = painterResource(R.drawable.bg_image_placeholder), + ) + } +} + +@Composable +private fun TitleAndChipSection( + title: String, + city: String, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(TuripTheme.spacing.small), + ) { + Text( + text = title, + style = TuripTheme.typography.title2, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.weight(1f), + ) + BookmarkRegionChip( + regionName = city, + modifier = Modifier.padding(horizontal = TuripTheme.spacing.small), + ) + } +} + +@Composable +private fun BookmarkRegionChip( + regionName: String, + modifier: Modifier = Modifier, +) { + Box( + modifier = + modifier + .wrapContentSize() + .background( + color = TuripTheme.colors.chipBackground, + shape = TuripTheme.shape.chip, + ) + .padding( + horizontal = TuripTheme.spacing.medium, + vertical = TuripTheme.spacing.extraSmall, + ), + ) { + Text( + text = regionName, + style = TuripTheme.typography.info1, + color = TuripTheme.colors.black, + ) + } +} + +@Composable +private fun ContentInformation( + item: BookmarkContent, + onRemoveBookmark: (contentId: Long) -> Unit, +) { + Row( + modifier = + Modifier + .fillMaxWidth() + .padding(start = TuripTheme.spacing.extraSmall), + verticalAlignment = Alignment.Top, + ) { + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(TuripTheme.spacing.small), + ) { + Text( + text = + stringResource( + R.string.region_result_video_description, + item.content.creator.channelName, + item.content.videoData.uploadedDate, + ), + style = TuripTheme.typography.info2, + color = TuripTheme.colors.gray03, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(TuripTheme.spacing.small), + ) { + ContentInfoItem( + text = item.tripDuration.toUiModel().toDisplayText(LocalContext.current), + iconPainterRes = R.drawable.ic_calendar, + ) + ContentInfoItem( + text = stringResource(R.string.all_total_place_count, item.tripPlaceCount), + iconPainterRes = R.drawable.ic_place, + ) + } + } + IconButton( + onClick = { onRemoveBookmark(item.content.id) }, + modifier = Modifier.size(48.dp), + ) { + Icon( + painter = painterResource(R.drawable.btn_bookmark_selected), + contentDescription = null, + tint = TuripTheme.colors.primary, + ) + } + } +} + +@Composable +private fun ContentInfoItem( + text: String, + @DrawableRes iconPainterRes: Int, + modifier: Modifier = Modifier, + textColor: Color = TuripTheme.colors.gray03, + iconTint: Color = TuripTheme.colors.gray02, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(TuripTheme.spacing.extraSmall), + modifier = modifier, + ) { + Icon( + painter = painterResource(iconPainterRes), + tint = iconTint, + contentDescription = null, + modifier = Modifier.size(12.dp), + ) + + Text( + text = text, + style = TuripTheme.typography.info2, + color = textColor, + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun BookmarkContentItemPreview() { + TuripTheme { + val content = + BookmarkContent( + content = + Content( + 1L, + Creator(1L, "채널명", ""), + VideoData("콘텐츠 제목이 길면 ...으로 표시되는 것을 확인 ㅇㅇㅇ", "thumbnail", "2026-02-12"), + City("대구"), + true, + ), + tripDuration = TripDuration(1, 2), + tripPlaceCount = 2, + ) + BookmarkContentItem( + content = content, + showDivider = true, + onContentClick = {}, + onRemoveBookmark = {}, + modifier = Modifier.fillMaxSize(), + ) + } +} diff --git a/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentItem.kt b/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentItem.kt index 5fdec3afb..d40f5c444 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentItem.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentItem.kt @@ -209,7 +209,7 @@ private fun BookmarkedContentItemPreview() { Content( 1L, Creator(1L, "채널명", ""), - VideoData("콘텐츠 제목이 길면 ...으로 표시되는 것을 확인 ㅇㅇㅇ", "thumbnail", "1박 2일"), + VideoData("콘텐츠 제목이 길면 ...으로 표시되는 것을 확인 ㅇㅇㅇ", "thumbnail", "2026-02-23"), City(""), true, ), diff --git a/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentSection.kt b/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentSection.kt index 2a336fce2..da104d17f 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentSection.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentSection.kt @@ -257,7 +257,7 @@ private fun BookmarkedContentSectionWithItemsPreview() { VideoData( "콘텐츠 제목이 길면 ...으로 표시되는 것을 확인 ㅇㅇㅇ", "thumbnail", - "1박 2일", + "2026-01-02", ), City(""), true, diff --git a/android/app/src/main/java/com/on/turip/ui/main/bookmarks/BookmarkContentFragment.kt b/android/app/src/main/java/com/on/turip/ui/main/bookmarks/BookmarkContentFragment.kt index 352cccfff..93e2144dd 100644 --- a/android/app/src/main/java/com/on/turip/ui/main/bookmarks/BookmarkContentFragment.kt +++ b/android/app/src/main/java/com/on/turip/ui/main/bookmarks/BookmarkContentFragment.kt @@ -1,54 +1,15 @@ package com.on.turip.ui.main.bookmarks -import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.fragment.app.viewModels -import com.google.android.material.snackbar.Snackbar -import com.on.turip.R import com.on.turip.databinding.FragmentFavoriteContentBinding -import com.on.turip.domain.bookmark.BookmarkContent -import com.on.turip.ui.common.ItemDividerDecoration import com.on.turip.ui.common.base.BaseFragment -import com.on.turip.ui.common.error.ErrorUiModel -import com.on.turip.ui.common.error.ErrorUiState -import com.on.turip.ui.common.error.toUiModel -import com.on.turip.ui.common.extensions.collectOnStarted -import com.on.turip.ui.login.LoginActivity -import com.on.turip.ui.trip.TripDetailActivity import dagger.hilt.android.AndroidEntryPoint -import timber.log.Timber @AndroidEntryPoint class BookmarkContentFragment : BaseFragment() { - private val viewModel: BookmarkContentViewModel by viewModels() - - private val favoriteContentAdapter: FavoriteContentAdapter by lazy { - FavoriteContentAdapter( - object : FavoriteContentViewHolder.FavoriteContentListener { - override fun onFavoriteClick( - contentId: Long, - isFavorite: Boolean, - ) { - Timber.d("컨텐츠 목록의 북마크 버튼을 클릭(contentId=$contentId)\n업데이트 된 북마크 상태 =${!isFavorite}") - viewModel.updateBookmark(contentId, isFavorite) - } - - override fun onFavoriteItemClick(contentId: Long) { - Timber.d("컨텐츠 목록의 아이템 클릭(contentId=$contentId)") - val intent: Intent = - TripDetailActivity.newIntent( - context = requireContext(), - contentId = contentId, - ) - startActivity(intent) - } - }, - ) - } - override fun inflateBinding( inflater: LayoutInflater, container: ViewGroup?, @@ -59,73 +20,7 @@ class BookmarkContentFragment : BaseFragment() { savedInstanceState: Bundle?, ) { super.onViewCreated(view, savedInstanceState) - setupAdapters() - setupObservers() - } - - private fun setupAdapters() { - binding.rvFavoriteContentContents.apply { - adapter = favoriteContentAdapter - addItemDecoration( - ItemDividerDecoration( - height = 1, - color = requireContext().getColor(R.color.gray_100_f0f0ee), - ), - ) - } - } - - private fun setupObservers() { - collectOnStarted(viewModel.uiState) { uiState: BookmarkContentUiState -> - if (uiState.isLoading) showLoading() - when { - uiState.errorUiState != ErrorUiState.None -> showErrorView(uiState.errorUiState) - uiState.isEmpty -> showEmptyView() - else -> showContents(uiState.bookmarkContents) - } - } - - collectOnStarted(viewModel.uiEffect) { uiEffect: BookmarkContentUiEffect -> - when (uiEffect) { - BookmarkContentUiEffect.NavigateToLogin -> { - navigateToLoginScreen() - } - - is BookmarkContentUiEffect.ShowError -> { - val uiModel: ErrorUiModel = - uiEffect.errorUiState.toUiModel() ?: return@collectOnStarted - view?.let { view: View -> - Snackbar - .make(view, uiModel.titleRes, Snackbar.LENGTH_INDEFINITE) - .apply { - setAction(uiModel.retryTextRes) { - viewModel.handleErrorRetryRequest(uiEffect.action) - } - }.show() - } - } - } - } - } - - private fun showLoading() { - binding.pbFavoriteContentLoading.visibility = View.VISIBLE - binding.clFavoriteContentEmpty.visibility = View.GONE - binding.clFavoriteContentNotEmpty.visibility = View.GONE - binding.customErrorView.visibility = View.GONE - } - - private fun showErrorView(errorUiState: ErrorUiState) { - binding.customErrorView.visibility = View.VISIBLE - binding.pbFavoriteContentLoading.visibility = View.GONE - binding.clFavoriteContentEmpty.visibility = View.GONE - binding.clFavoriteContentNotEmpty.visibility = View.GONE - - binding.customErrorView.apply { - visibility = View.VISIBLE - showErrorView(errorUiState) - setOnRetryClickListener { viewModel.loadBookmarkContents() } - } + showEmptyView() } private fun showEmptyView() { @@ -135,36 +30,6 @@ class BookmarkContentFragment : BaseFragment() { binding.clFavoriteContentNotEmpty.visibility = View.GONE } - private fun showContents(bookmarkContents: List) { - binding.customErrorView.visibility = View.GONE - binding.pbFavoriteContentLoading.visibility = View.GONE - - binding.clFavoriteContentNotEmpty.visibility = View.VISIBLE - binding.clFavoriteContentEmpty.visibility = View.GONE - favoriteContentAdapter.submitList(bookmarkContents) - } - - private fun navigateToLoginScreen() { - val intent: Intent = - LoginActivity - .newIntent(requireActivity()) - .apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } - startActivity(intent) - requireActivity().finish() - } - - override fun onResume() { - super.onResume() - viewModel.loadBookmarkContents() - } - - override fun onHiddenChanged(hidden: Boolean) { - super.onHiddenChanged(hidden) - if (!hidden) { - viewModel.loadBookmarkContents() - } - } - companion object { fun instance(): BookmarkContentFragment = BookmarkContentFragment() } diff --git a/android/app/src/main/java/com/on/turip/ui/main/bookmarks/BookmarkContentUiEffect.kt b/android/app/src/main/java/com/on/turip/ui/main/bookmarks/BookmarkContentUiEffect.kt deleted file mode 100644 index f1ea4e027..000000000 --- a/android/app/src/main/java/com/on/turip/ui/main/bookmarks/BookmarkContentUiEffect.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.on.turip.ui.main.bookmarks - -import com.on.turip.ui.common.error.ErrorUiState - -sealed interface BookmarkContentUiEffect { - data object NavigateToLogin : BookmarkContentUiEffect - - data class ShowError( - val errorUiState: ErrorUiState, - val action: BookmarkContentRetryAction, - ) : BookmarkContentUiEffect -} - -sealed interface BookmarkContentRetryAction { - data class UpdateBookmark( - val contentId: Long, - val isBookmarked: Boolean, - ) : BookmarkContentRetryAction -} diff --git a/android/app/src/main/java/com/on/turip/ui/main/bookmarks/BookmarkContentViewModel.kt b/android/app/src/main/java/com/on/turip/ui/main/bookmarks/BookmarkContentViewModel.kt deleted file mode 100644 index 018ff3d71..000000000 --- a/android/app/src/main/java/com/on/turip/ui/main/bookmarks/BookmarkContentViewModel.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.on.turip.ui.main.bookmarks - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.on.turip.core.result.ErrorType -import com.on.turip.core.result.onFailure -import com.on.turip.core.result.onSuccess -import com.on.turip.domain.bookmark.PagedBookmarkContents -import com.on.turip.domain.bookmark.repository.BookmarkRepository -import com.on.turip.domain.bookmark.usecase.UpdateBookmarkUseCase -import com.on.turip.ui.common.error.ErrorUiState -import com.on.turip.ui.common.error.UiError -import com.on.turip.ui.common.error.toUiError -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.receiveAsFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import timber.log.Timber -import javax.inject.Inject - -@HiltViewModel -class BookmarkContentViewModel @Inject constructor( - private val bookmarkRepository: BookmarkRepository, - private val updateBookmarkUseCase: UpdateBookmarkUseCase, -) : ViewModel() { - private val _uiState: MutableStateFlow = - MutableStateFlow(BookmarkContentUiState.Idle) - val uiState: StateFlow = _uiState.asStateFlow() - - private val _uiEffect: Channel = Channel(Channel.BUFFERED) - val uiEffect: Flow = _uiEffect.receiveAsFlow() - - init { - loadBookmarkContents() - } - - fun loadBookmarkContents() { - viewModelScope.launch { - _uiState.update { it.copy(isLoading = true) } - - bookmarkRepository - .loadBookmarks(10, 0L) - .onSuccess { result: PagedBookmarkContents -> - Timber.d("북마크 목록 조회 성공") - _uiState.update { - it.copy( - isLoading = false, - bookmarkContents = result.bookmarkContents, - errorUiState = ErrorUiState.None, - ) - } - }.onFailure { errorType: ErrorType -> - Timber.e("북마크 목록 조회 에러 발생") - val uiError: UiError = errorType.toUiError() - if (uiError is UiError.Global) { - when (uiError) { - UiError.Global.Network -> { - _uiState.update { - it.copy(isLoading = false, errorUiState = ErrorUiState.Network) - } - } - - UiError.Global.Server -> { - _uiState.update { - it.copy(isLoading = false, errorUiState = ErrorUiState.Server) - } - } - - UiError.Global.TokenExpired -> { - _uiState.update { it.copy(isLoading = false) } - _uiEffect.send(BookmarkContentUiEffect.NavigateToLogin) - } - } - } - } - } - } - - fun updateBookmark( - contentId: Long, - isBookmarked: Boolean, - ) { - val updatedBookmark: Boolean = !isBookmarked - - viewModelScope.launch { - updateBookmarkUseCase(updatedBookmark, contentId) - .onSuccess { - Timber.d("북마크 목록 화면, 북마크 클릭(contentId=$contentId, updatedBookmark = $updatedBookmark") - - _uiState.update { state: BookmarkContentUiState -> - state.copy( - isLoading = false, - bookmarkContents = state.bookmarkContents.filter { it.content.id != contentId }, - errorUiState = ErrorUiState.None, - ) - } - }.onFailure { errorType: ErrorType -> - Timber.e("북마크 목록 화면, 북마크 클릭 실패(contentId=$contentId, originBookmark = $isBookmarked)") - _uiState.update { it.copy(isLoading = false) } - val uiError: UiError = errorType.toUiError() - if (uiError is UiError.Global) { - when (uiError) { - UiError.Global.Network -> { - _uiEffect.send( - BookmarkContentUiEffect.ShowError( - errorUiState = ErrorUiState.Network, - action = - BookmarkContentRetryAction.UpdateBookmark( - contentId = contentId, - isBookmarked = isBookmarked, - ), - ), - ) - } - - UiError.Global.Server -> { - _uiEffect.send( - BookmarkContentUiEffect.ShowError( - errorUiState = ErrorUiState.Server, - action = - BookmarkContentRetryAction.UpdateBookmark( - contentId = contentId, - isBookmarked = isBookmarked, - ), - ), - ) - } - - UiError.Global.TokenExpired -> { - _uiEffect.send(BookmarkContentUiEffect.NavigateToLogin) - } - } - } - } - } - } - - fun handleErrorRetryRequest(action: BookmarkContentRetryAction) { - when (action) { - is BookmarkContentRetryAction.UpdateBookmark -> { - updateBookmark(action.contentId, action.isBookmarked) - } - } - } -} diff --git a/android/app/src/main/java/com/on/turip/ui/mypage/MyPageActivity.kt b/android/app/src/main/java/com/on/turip/ui/mypage/MyPageActivity.kt index 5a3383fa5..45072800e 100644 --- a/android/app/src/main/java/com/on/turip/ui/mypage/MyPageActivity.kt +++ b/android/app/src/main/java/com/on/turip/ui/mypage/MyPageActivity.kt @@ -9,6 +9,7 @@ import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.net.toUri import com.on.turip.R +import com.on.turip.ui.bookmarks.BookmarkContentActivity import com.on.turip.ui.common.extensions.safeStartActivityWithToast import com.on.turip.ui.compose.designsystem.theme.TuripTheme import com.on.turip.ui.compose.mypage.MyPageScreen @@ -27,8 +28,10 @@ class MyPageActivity : AppCompatActivity() { setContent { TuripTheme { MyPageScreen( - // 화면 구현 필요 - navigateToAllBookmarkContents = {}, + navigateToAllBookmarkContents = { + val intent = BookmarkContentActivity.newIntent(this) + startActivity(intent) + }, navigateToContent = { contentId: Long -> Timber.d("마이페이지 북마크 콘텐츠 클릭(contentId=$contentId)") val intent: Intent = diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index a51abe481..2767f0a9e 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -67,6 +67,8 @@ 새로고침 + 저장한 콘텐츠 + 콘텐츠 %d개 컨텐츠를 저장 해보세요! 원하는 동선이 담긴\n여행 일정을 저장해 보세요. From dd128811650fecb253a7081698e9d5348274eee1 Mon Sep 17 00:00:00 2001 From: yrsel Date: Tue, 24 Feb 2026 01:01:03 +0900 Subject: [PATCH 02/26] =?UTF-8?q?feat:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=96=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/on/turip/domain/common/paging/Page.kt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 android/app/src/main/java/com/on/turip/domain/common/paging/Page.kt diff --git a/android/app/src/main/java/com/on/turip/domain/common/paging/Page.kt b/android/app/src/main/java/com/on/turip/domain/common/paging/Page.kt new file mode 100644 index 000000000..011e74684 --- /dev/null +++ b/android/app/src/main/java/com/on/turip/domain/common/paging/Page.kt @@ -0,0 +1,6 @@ +package com.on.turip.domain.common.paging + +data class Page( + val items: List, + val hasNext: Boolean, +) From 155262152a6d8bc1fcd83c2237aed0b691d7bb52 Mon Sep 17 00:00:00 2001 From: yrsel Date: Tue, 24 Feb 2026 01:01:32 +0900 Subject: [PATCH 03/26] =?UTF-8?q?feat:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20Ui/Presentation=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/on/turip/ui/common/paging/PagingLoadMode.kt | 9 +++++++++ .../com/on/turip/ui/common/paging/PagingState.kt | 13 +++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 android/app/src/main/java/com/on/turip/ui/common/paging/PagingLoadMode.kt create mode 100644 android/app/src/main/java/com/on/turip/ui/common/paging/PagingState.kt diff --git a/android/app/src/main/java/com/on/turip/ui/common/paging/PagingLoadMode.kt b/android/app/src/main/java/com/on/turip/ui/common/paging/PagingLoadMode.kt new file mode 100644 index 000000000..7b937907d --- /dev/null +++ b/android/app/src/main/java/com/on/turip/ui/common/paging/PagingLoadMode.kt @@ -0,0 +1,9 @@ +package com.on.turip.ui.common.paging + +import androidx.compose.runtime.Immutable + +@Immutable +enum class PagingLoadMode { + REFRESH, + APPEND, +} diff --git a/android/app/src/main/java/com/on/turip/ui/common/paging/PagingState.kt b/android/app/src/main/java/com/on/turip/ui/common/paging/PagingState.kt new file mode 100644 index 000000000..153ac917b --- /dev/null +++ b/android/app/src/main/java/com/on/turip/ui/common/paging/PagingState.kt @@ -0,0 +1,13 @@ +package com.on.turip.ui.common.paging + +import androidx.compose.runtime.Immutable +import com.on.turip.ui.common.error.ErrorUiState +import kotlinx.collections.immutable.ImmutableList + +@Immutable +data class PagingState( + val items: ImmutableList, + val hasNext: Boolean, + val isAppending: Boolean, + val errorUiState: ErrorUiState, +) From 5c00a164856e798be6fd9bec11cbe293b318cd3e Mon Sep 17 00:00:00 2001 From: yrsel Date: Tue, 24 Feb 2026 01:03:09 +0900 Subject: [PATCH 04/26] =?UTF-8?q?feat:=20=EB=B6=81=EB=A7=88=ED=81=AC=20?= =?UTF-8?q?=EC=BD=98=ED=85=90=EC=B8=A0=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20API=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=20=EC=98=81=EC=97=AD=20=EB=B0=98=ED=99=98=EA=B0=92=20=EB=B0=8F?= =?UTF-8?q?=20=EB=A7=A4=ED=8D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/on/turip/data/bookmarks/BookmarkMapper.kt | 10 +++++----- .../bookmarks/repository/DefaultBookmarkRepository.kt | 5 +++-- .../on/turip/domain/bookmark/PagedBookmarkContents.kt | 6 ------ .../domain/bookmark/repository/BookmarkRepository.kt | 5 +++-- .../com/on/turip/ui/compose/mypage/MyPageViewModel.kt | 9 +++++---- 5 files changed, 16 insertions(+), 19 deletions(-) delete mode 100644 android/app/src/main/java/com/on/turip/domain/bookmark/PagedBookmarkContents.kt diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/BookmarkMapper.kt b/android/app/src/main/java/com/on/turip/data/bookmarks/BookmarkMapper.kt index b5023c6e9..bdc7fb284 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmarks/BookmarkMapper.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmarks/BookmarkMapper.kt @@ -5,14 +5,14 @@ import com.on.turip.data.bookmarks.dto.BookmarkContentResponse import com.on.turip.data.bookmarks.dto.BookmarkContentsResponse import com.on.turip.data.content.toDomain import com.on.turip.domain.bookmark.BookmarkContent -import com.on.turip.domain.bookmark.PagedBookmarkContents +import com.on.turip.domain.common.paging.Page fun Long.toRequestDto(): BookmarkAddRequest = BookmarkAddRequest(contentId = this) -fun BookmarkContentsResponse.toDomain(): PagedBookmarkContents = - PagedBookmarkContents( - bookmarkContents = contents.map { it.toDomain() }, - loadable = loadable, +fun BookmarkContentsResponse.toDomain(): Page = + Page( + items = contents.map { it.toDomain() }, + hasNext = loadable, ) fun BookmarkContentResponse.toDomain(): BookmarkContent = diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/repository/DefaultBookmarkRepository.kt b/android/app/src/main/java/com/on/turip/data/bookmarks/repository/DefaultBookmarkRepository.kt index a9d101b90..60f137f5b 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmarks/repository/DefaultBookmarkRepository.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmarks/repository/DefaultBookmarkRepository.kt @@ -5,8 +5,9 @@ import com.on.turip.core.result.mapCatching import com.on.turip.data.bookmarks.datasource.BookmarkRemoteDataSource import com.on.turip.data.bookmarks.toDomain import com.on.turip.data.bookmarks.toRequestDto -import com.on.turip.domain.bookmark.PagedBookmarkContents +import com.on.turip.domain.bookmark.BookmarkContent import com.on.turip.domain.bookmark.repository.BookmarkRepository +import com.on.turip.domain.common.paging.Page import javax.inject.Inject class DefaultBookmarkRepository @Inject constructor( @@ -20,5 +21,5 @@ class DefaultBookmarkRepository @Inject constructor( override suspend fun loadBookmarks( size: Int, lastId: Long, - ): TuripResult = bookmarkRemoteDataSource.getBookmarks(size, lastId).mapCatching { it.toDomain() } + ): TuripResult> = bookmarkRemoteDataSource.getBookmarks(size, lastId).mapCatching { it.toDomain() } } diff --git a/android/app/src/main/java/com/on/turip/domain/bookmark/PagedBookmarkContents.kt b/android/app/src/main/java/com/on/turip/domain/bookmark/PagedBookmarkContents.kt deleted file mode 100644 index 3509010d9..000000000 --- a/android/app/src/main/java/com/on/turip/domain/bookmark/PagedBookmarkContents.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.on.turip.domain.bookmark - -data class PagedBookmarkContents( - val bookmarkContents: List, - val loadable: Boolean, -) diff --git a/android/app/src/main/java/com/on/turip/domain/bookmark/repository/BookmarkRepository.kt b/android/app/src/main/java/com/on/turip/domain/bookmark/repository/BookmarkRepository.kt index 256caa5a5..5c34e213c 100644 --- a/android/app/src/main/java/com/on/turip/domain/bookmark/repository/BookmarkRepository.kt +++ b/android/app/src/main/java/com/on/turip/domain/bookmark/repository/BookmarkRepository.kt @@ -1,7 +1,8 @@ package com.on.turip.domain.bookmark.repository import com.on.turip.core.result.TuripResult -import com.on.turip.domain.bookmark.PagedBookmarkContents +import com.on.turip.domain.bookmark.BookmarkContent +import com.on.turip.domain.common.paging.Page interface BookmarkRepository { suspend fun createBookmark(contentId: Long): TuripResult @@ -11,5 +12,5 @@ interface BookmarkRepository { suspend fun loadBookmarks( size: Int, lastId: Long, - ): TuripResult + ): TuripResult> } diff --git a/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt index 90eafe360..677f2ac6a 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt @@ -7,8 +7,9 @@ import com.on.turip.core.result.onFailure import com.on.turip.core.result.onSuccess import com.on.turip.domain.accounts.Account import com.on.turip.domain.accounts.AccountRepository -import com.on.turip.domain.bookmark.PagedBookmarkContents +import com.on.turip.domain.bookmark.BookmarkContent import com.on.turip.domain.bookmark.repository.BookmarkRepository +import com.on.turip.domain.common.paging.Page import com.on.turip.domain.login.MemberRepository import com.on.turip.domain.setting.PrivacyPolicy import com.on.turip.domain.userstorage.repository.UserStorageRepository @@ -18,6 +19,7 @@ import com.on.turip.ui.common.error.toUiError import com.on.turip.ui.compose.mypage.model.InquiryMail import com.on.turip.ui.compose.mypage.util.AppEnvironmentInfoProvider import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -30,7 +32,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import timber.log.Timber -import javax.inject.Inject @HiltViewModel class MyPageViewModel @Inject constructor( @@ -74,10 +75,10 @@ class MyPageViewModel @Inject constructor( bookmarkRepository .loadBookmarks(10, 0L) - .onSuccess { result: PagedBookmarkContents -> + .onSuccess { result: Page -> Timber.d("마이페이지 북마크 목록 조회 성공") _uiState.update { - it.copy(bookmarkContentState = MyPageSectionState.Success(result.bookmarkContents.toImmutableList())) + it.copy(bookmarkContentState = MyPageSectionState.Success(result.items.toImmutableList())) } }.onFailure { Timber.e("마이페이지 북마크 목록 조회 에러 발생") From 5288e0db42b7c8d0e94ebaa455f43e63d28b1fa0 Mon Sep 17 00:00:00 2001 From: yrsel Date: Tue, 24 Feb 2026 01:04:49 +0900 Subject: [PATCH 05/26] =?UTF-8?q?feat:=20=EC=BD=98=ED=85=90=EC=B8=A0=20?= =?UTF-8?q?=EB=B6=81=EB=A7=88=ED=81=AC=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bookmarks/BookmarkContentScreen.kt | 130 ++++++++++++- .../bookmarks/BookmarkContentUiState.kt | 14 +- .../bookmarks/BookmarkContentViewModel.kt | 173 +++++++++++++++--- android/app/src/main/res/values/strings.xml | 1 + 4 files changed, 278 insertions(+), 40 deletions(-) diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt index 86454e446..7865fe230 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt @@ -6,6 +6,7 @@ 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.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -15,15 +16,19 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalResources @@ -43,11 +48,14 @@ import com.on.turip.domain.region.City import com.on.turip.domain.trip.TripDuration import com.on.turip.ui.common.error.ErrorUiState import com.on.turip.ui.common.extensions.showSnackbarWithAction +import com.on.turip.ui.common.paging.PagingState import com.on.turip.ui.compose.bookmarks.component.BookmarkContentAppBar import com.on.turip.ui.compose.bookmarks.component.BookmarkContentItem import com.on.turip.ui.compose.designsystem.component.ErrorScreen import com.on.turip.ui.compose.designsystem.theme.TuripTheme import kotlinx.collections.immutable.persistentListOf +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter @Composable fun BookmarkContentScreen( @@ -72,7 +80,7 @@ fun BookmarkContentScreen( snackbarHostState.showSnackbarWithAction( message = resources.getString(R.string.my_page_snackbar_bookmark_remove_failed), actionLabel = resources.getString(R.string.my_page_snackbar_bookmark_remove_failed_action), - onAction = viewModel::loadBookmarkContents, + onAction = viewModel::refreshBookmarkContents, ) } } @@ -92,9 +100,10 @@ fun BookmarkContentScreen( ) { BookmarkContentContent( uiState = uiState, - onRetryClick = viewModel::loadBookmarkContents, + onRetryClick = viewModel::refreshBookmarkContents, onContentClick = navigateToContent, onBookmarkClick = viewModel::removeBookmark, + loadMoreContents = viewModel::loadMoreContents, ) } } @@ -106,6 +115,7 @@ private fun BookmarkContentContent( onRetryClick: () -> Unit, onContentClick: (contentId: Long) -> Unit, onBookmarkClick: (contentId: Long) -> Unit, + loadMoreContents: () -> Unit, ) { when { uiState.isLoading -> { @@ -125,9 +135,10 @@ private fun BookmarkContentContent( BookmarkContentEmpty() } else { BookmarkContents( - uiState = uiState, + pagingState = uiState.bookmarkContents, onContentClick = onContentClick, onBookmarkClick = onBookmarkClick, + loadMore = loadMoreContents, ) } } @@ -176,15 +187,43 @@ private fun BookmarkContentEmpty() { @Composable private fun BookmarkContents( - uiState: BookmarkContentUiState, + pagingState: PagingState, onContentClick: (contentId: Long) -> Unit, onBookmarkClick: (contentId: Long) -> Unit, + loadMore: () -> Unit, ) { + val listState = rememberLazyListState() + val threshold = 1 + val shouldLoadMore by remember { + derivedStateOf { + if (!pagingState.hasNext || pagingState.isAppending || pagingState.errorUiState != ErrorUiState.None || + pagingState.items.isEmpty() + ) { + return@derivedStateOf false + } + + val layoutInfo = listState.layoutInfo + val totalCount = layoutInfo.totalItemsCount + val lastVisibleIndex = + layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: return@derivedStateOf false + val lastIndex = totalCount - 1 + + lastVisibleIndex >= lastIndex - threshold + } + } + + LaunchedEffect(Unit) { + snapshotFlow { shouldLoadMore } + .distinctUntilChanged() + .filter { it } + .collect { loadMore() } + } + Text( text = stringResource( R.string.bookmark_content_count, - uiState.bookmarkContents.size, + pagingState.items.size, ), textAlign = TextAlign.End, style = TuripTheme.typography.info2, @@ -203,20 +242,69 @@ private fun BookmarkContents( ) LazyColumn( + state = listState, contentPadding = PaddingValues(TuripTheme.spacing.medium), verticalArrangement = Arrangement.spacedBy(TuripTheme.spacing.medium), ) { itemsIndexed( - items = uiState.bookmarkContents, + items = pagingState.items, key = { _, item -> item.content.id }, ) { index, content -> BookmarkContentItem( content = content, - showDivider = index != uiState.bookmarkContents.lastIndex, + showDivider = index != pagingState.items.lastIndex, onContentClick = onContentClick, onRemoveBookmark = onBookmarkClick, ) } + + if (pagingState.isAppending) { + item { + Box( + modifier = + Modifier + .fillMaxWidth() + .padding(16.dp), + contentAlignment = Alignment.Center, + ) { + CircularProgressIndicator( + modifier = Modifier.size(24.dp), + color = TuripTheme.colors.black, + ) + } + } + } else if (pagingState.errorUiState != ErrorUiState.None) { + item { + LoadMoreError( + onRetryClick = loadMore, + ) + } + } + } +} + +@Composable +private fun LoadMoreError(onRetryClick: () -> Unit) { + Row( + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = TuripTheme.spacing.large), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = stringResource(R.string.bookmark_content_load_more_fail_title), + style = TuripTheme.typography.info1, + modifier = Modifier.weight(1f), + ) + + TextButton(onClick = onRetryClick) { + Text( + text = stringResource(R.string.retry), + style = TuripTheme.typography.info2, + color = TuripTheme.colors.gray04, + ) + } } } @@ -229,6 +317,7 @@ private fun BookmarkContentLoadingPreview() { onRetryClick = {}, onContentClick = {}, onBookmarkClick = {}, + loadMoreContents = { }, ) } } @@ -241,12 +330,19 @@ private fun BookmarkContentEmptyPreview() { uiState = BookmarkContentUiState( isLoading = false, - bookmarkContents = persistentListOf(), + bookmarkContents = + PagingState( + items = persistentListOf(), + hasNext = false, + isAppending = false, + errorUiState = ErrorUiState.None, + ), errorUiState = ErrorUiState.None, ), onRetryClick = {}, onContentClick = {}, onBookmarkClick = {}, + loadMoreContents = { }, ) } } @@ -259,12 +355,19 @@ private fun BookmarkContentErrorPreview() { uiState = BookmarkContentUiState( isLoading = false, - bookmarkContents = persistentListOf(), + bookmarkContents = + PagingState( + items = persistentListOf(), + hasNext = false, + isAppending = false, + errorUiState = ErrorUiState.None, + ), errorUiState = ErrorUiState.Network, ), onRetryClick = {}, onContentClick = {}, onBookmarkClick = {}, + loadMoreContents = { }, ) } } @@ -304,12 +407,19 @@ private fun BookmarkContentErrorSuccess() { uiState = BookmarkContentUiState( isLoading = false, - bookmarkContents = contents, + bookmarkContents = + PagingState( + items = contents, + hasNext = false, + isAppending = false, + errorUiState = ErrorUiState.None, + ), errorUiState = ErrorUiState.None, ), onRetryClick = {}, onContentClick = {}, onBookmarkClick = {}, + loadMoreContents = {}, ) } } diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiState.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiState.kt index 4a4a28b6f..22cbda516 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiState.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiState.kt @@ -3,23 +3,29 @@ package com.on.turip.ui.compose.bookmarks import androidx.compose.runtime.Immutable import com.on.turip.domain.bookmark.BookmarkContent import com.on.turip.ui.common.error.ErrorUiState -import kotlinx.collections.immutable.ImmutableList +import com.on.turip.ui.common.paging.PagingState import kotlinx.collections.immutable.persistentListOf @Immutable data class BookmarkContentUiState( val isLoading: Boolean, - val bookmarkContents: ImmutableList, + val bookmarkContents: PagingState, val errorUiState: ErrorUiState, ) { val isEmpty: Boolean - get() = bookmarkContents.isEmpty() && this != Idle + get() = !isLoading && bookmarkContents.items.isEmpty() && errorUiState == ErrorUiState.None companion object { val Idle: BookmarkContentUiState = BookmarkContentUiState( isLoading = true, - bookmarkContents = persistentListOf(), + bookmarkContents = + PagingState( + items = persistentListOf(), + hasNext = false, + isAppending = false, + errorUiState = ErrorUiState.None, + ), errorUiState = ErrorUiState.None, ) } diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt index 1e93fb250..fe7f50d04 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt @@ -5,11 +5,14 @@ import androidx.lifecycle.viewModelScope import com.on.turip.core.result.ErrorType import com.on.turip.core.result.onFailure import com.on.turip.core.result.onSuccess -import com.on.turip.domain.bookmark.PagedBookmarkContents +import com.on.turip.domain.bookmark.BookmarkContent import com.on.turip.domain.bookmark.repository.BookmarkRepository +import com.on.turip.domain.common.paging.Page import com.on.turip.ui.common.error.ErrorUiState import com.on.turip.ui.common.error.UiError import com.on.turip.ui.common.error.toUiError +import com.on.turip.ui.common.paging.PagingLoadMode +import com.on.turip.ui.common.paging.PagingState import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import kotlinx.collections.immutable.toImmutableList @@ -37,45 +40,159 @@ class BookmarkContentViewModel @Inject constructor( val uiEffect: Flow = _uiEffect.receiveAsFlow() init { - loadBookmarkContents() + refreshBookmarkContents() } - fun loadBookmarkContents() { + fun refreshBookmarkContents() { + loadBookmarkContents(PagingLoadMode.REFRESH) + } + + fun loadMoreContents() { + loadBookmarkContents(PagingLoadMode.APPEND) + } + + private fun loadBookmarkContents(loadMode: PagingLoadMode) { viewModelScope.launch { - _uiState.update { it.copy(isLoading = true) } + when (loadMode) { + PagingLoadMode.REFRESH -> { + _uiState.update { state -> + state.copy( + isLoading = true, + errorUiState = ErrorUiState.None, + bookmarkContents = + state.bookmarkContents.copy( + isAppending = false, + errorUiState = ErrorUiState.None, + ), + ) + } + } + + PagingLoadMode.APPEND -> { + val pagingState = uiState.value.bookmarkContents + if (!pagingState.hasNext || pagingState.items.isEmpty() || pagingState.isAppending) return@launch + + _uiState.update { state -> + state.copy( + isLoading = false, + bookmarkContents = + state.bookmarkContents.copy( + isAppending = true, + errorUiState = ErrorUiState.None, + ), + errorUiState = ErrorUiState.None, + ) + } + } + } + + val lastItemId = + when (loadMode) { + PagingLoadMode.REFRESH -> { + 0L + } + + PagingLoadMode.APPEND -> { + uiState.value.bookmarkContents.items + .last() + .content.id + } + } bookmarkRepository - .loadBookmarks(10, 0L) - .onSuccess { result: PagedBookmarkContents -> - Timber.d("북마크 화면 조회 성공") + .loadBookmarks(PAGE_SIZE, lastItemId) + .onSuccess { result: Page -> + Timber.d("북마크 화면 조회 성공 mode =$loadMode") _uiState.update { state -> + val newItems = + when (loadMode) { + PagingLoadMode.REFRESH -> result.items.toImmutableList() + PagingLoadMode.APPEND -> (state.bookmarkContents.items + result.items).toImmutableList() + } state.copy( isLoading = false, - bookmarkContents = result.bookmarkContents.toImmutableList(), + bookmarkContents = + PagingState( + items = newItems, + hasNext = result.hasNext, + isAppending = false, + errorUiState = ErrorUiState.None, + ), errorUiState = ErrorUiState.None, ) } }.onFailure { errorType: ErrorType -> - Timber.e("북마크 화면 조회 에러") - val uiError: UiError = errorType.toUiError() - if (uiError is UiError.Global) { - when (uiError) { - UiError.Global.Network -> { - _uiState.update { - it.copy(isLoading = false, errorUiState = ErrorUiState.Network) + Timber.e("북마크 화면 에러 loadMode = $loadMode") + when (loadMode) { + PagingLoadMode.REFRESH -> { + val uiError: UiError = errorType.toUiError() + if (uiError is UiError.Global) { + when (uiError) { + UiError.Global.Network -> { + _uiState.update { + it.copy( + isLoading = false, + errorUiState = ErrorUiState.Network, + ) + } + } + + UiError.Global.Server -> { + _uiState.update { + it.copy( + isLoading = false, + errorUiState = ErrorUiState.Server, + ) + } + } + + UiError.Global.TokenExpired -> { + _uiState.update { it.copy(isLoading = false) } + _uiEffect.send(BookmarkContentUiEffect.NavigateToLogin) + } } } + } - UiError.Global.Server -> { - _uiState.update { - it.copy(isLoading = false, errorUiState = ErrorUiState.Server) + PagingLoadMode.APPEND -> { + val uiError: UiError = errorType.toUiError() + if (uiError is UiError.Global) { + when (uiError) { + UiError.Global.Network -> { + _uiState.update { + it.copy( + bookmarkContents = + it.bookmarkContents.copy( + isAppending = false, + errorUiState = ErrorUiState.Network, + ), + ) + } + } + + UiError.Global.Server -> { + _uiState.update { + it.copy( + bookmarkContents = + it.bookmarkContents.copy( + isAppending = false, + errorUiState = ErrorUiState.Server, + ), + ) + } + } + + UiError.Global.TokenExpired -> { + _uiState.update { + it.copy( + bookmarkContents = + it.bookmarkContents.copy(isAppending = false), + ) + } + _uiEffect.send(BookmarkContentUiEffect.NavigateToLogin) + } } } - - UiError.Global.TokenExpired -> { - _uiState.update { it.copy(isLoading = false) } - _uiEffect.send(BookmarkContentUiEffect.NavigateToLogin) - } } } } @@ -100,13 +217,13 @@ class BookmarkContentViewModel @Inject constructor( val contents = _uiState.value.bookmarkContents // 이미 UI 제거 완료된 상태 (API 호출 완료) - if (contents.none { it.content.id == contentId }) return@withLock false + if (contents.items.none { it.content.id == contentId }) return@withLock false val updated = - contents.filter { it.content.id != contentId }.toImmutableList() + contents.items.filter { it.content.id != contentId }.toImmutableList() _uiState.update { state -> - state.copy(bookmarkContents = updated) + state.copy(bookmarkContents = state.bookmarkContents.copy(items = updated)) } true @@ -125,4 +242,8 @@ class BookmarkContentViewModel @Inject constructor( } } } + + companion object { + private const val PAGE_SIZE = 20 + } } diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 2767f0a9e..e27d2ed48 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -71,6 +71,7 @@ 콘텐츠 %d개 컨텐츠를 저장 해보세요! 원하는 동선이 담긴\n여행 일정을 저장해 보세요. + 데이터를 불러올 수 없습니다. 잠시 후 재시도 해주세요 튜립에 장소를 추가 해보세요! From 9c659ac4e9c7be9b8f290e21e8e3f94beaea0eba Mon Sep 17 00:00:00 2001 From: yrsel Date: Tue, 24 Feb 2026 01:34:43 +0900 Subject: [PATCH 06/26] =?UTF-8?q?feat:=20=EB=B6=81=EB=A7=88=ED=81=AC=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=BD=98=ED=85=90=EC=B8=A0=EC=99=80=20?= =?UTF-8?q?=EB=A7=88=EC=9D=B4=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=B6=81?= =?UTF-8?q?=EB=A7=88=ED=81=AC=20=EC=BD=98=ED=85=90=EC=B8=A0=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=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 --- .../ui/bookmarks/BookmarkContentActivity.kt | 15 ++++++++++++ .../bookmarks/BookmarkContentScreen.kt | 5 ++++ .../bookmarks/BookmarkContentUiEffect.kt | 2 ++ .../bookmarks/BookmarkContentViewModel.kt | 4 +++- .../com/on/turip/ui/mypage/MyPageActivity.kt | 24 ++++++++++++++++++- 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt b/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt index eea4a2e15..7b0243c23 100644 --- a/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt +++ b/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt @@ -1,5 +1,6 @@ package com.on.turip.ui.bookmarks +import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle @@ -14,6 +15,8 @@ import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class BookmarkContentActivity : AppCompatActivity() { + private var hasBookmarkChanges = false + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() @@ -38,11 +41,23 @@ class BookmarkContentActivity : AppCompatActivity() { TripDetailActivity.newIntent(context = this, contentId = contentId) startActivity(intent) }, + onBookmarkChanged = { + hasBookmarkChanges = true + }, ) } } } + override fun finish() { + val data = + Intent().apply { + putExtra("BOOKMARK_CONTENT_HAS_BOOKMARK_CHANGES_FLAG", hasBookmarkChanges) + } + setResult(Activity.RESULT_OK, data) + super.finish() + } + companion object { fun newIntent(context: Context): Intent = Intent(context, BookmarkContentActivity::class.java) } diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt index 7865fe230..60083c46c 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt @@ -62,6 +62,7 @@ fun BookmarkContentScreen( navigateToBack: () -> Unit, navigateToLogin: () -> Unit, navigateToContent: (contentId: Long) -> Unit, + onBookmarkChanged: () -> Unit, viewModel: BookmarkContentViewModel = hiltViewModel(), ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -76,6 +77,10 @@ fun BookmarkContentScreen( navigateToLogin() } + BookmarkContentUiEffect.BookmarkRemoved -> { + onBookmarkChanged() + } + BookmarkContentUiEffect.ShowBookmarkRemoveFailed -> { snackbarHostState.showSnackbarWithAction( message = resources.getString(R.string.my_page_snackbar_bookmark_remove_failed), diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiEffect.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiEffect.kt index cbfd3c5b0..b489dd9cb 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiEffect.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiEffect.kt @@ -4,4 +4,6 @@ sealed interface BookmarkContentUiEffect { data object NavigateToLogin : BookmarkContentUiEffect data object ShowBookmarkRemoveFailed : BookmarkContentUiEffect + + data object BookmarkRemoved : BookmarkContentUiEffect } diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt index fe7f50d04..af33155ad 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt @@ -233,7 +233,9 @@ class BookmarkContentViewModel @Inject constructor( bookmarkRepository .deleteBookmark(contentId) - .onFailure { + .onSuccess { + _uiEffect.send(BookmarkContentUiEffect.BookmarkRemoved) + }.onFailure { _uiEffect.send(BookmarkContentUiEffect.ShowBookmarkRemoveFailed) } } finally { diff --git a/android/app/src/main/java/com/on/turip/ui/mypage/MyPageActivity.kt b/android/app/src/main/java/com/on/turip/ui/mypage/MyPageActivity.kt index 45072800e..a5d9fc8f6 100644 --- a/android/app/src/main/java/com/on/turip/ui/mypage/MyPageActivity.kt +++ b/android/app/src/main/java/com/on/turip/ui/mypage/MyPageActivity.kt @@ -1,11 +1,14 @@ package com.on.turip.ui.mypage +import android.app.Activity import android.content.Context import android.content.Intent import android.net.Uri import android.os.Bundle import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.core.net.toUri import com.on.turip.R @@ -13,6 +16,7 @@ import com.on.turip.ui.bookmarks.BookmarkContentActivity import com.on.turip.ui.common.extensions.safeStartActivityWithToast import com.on.turip.ui.compose.designsystem.theme.TuripTheme import com.on.turip.ui.compose.mypage.MyPageScreen +import com.on.turip.ui.compose.mypage.MyPageViewModel import com.on.turip.ui.compose.mypage.model.InquiryMail import com.on.turip.ui.login.LoginActivity import com.on.turip.ui.trip.TripDetailActivity @@ -21,6 +25,8 @@ import timber.log.Timber @AndroidEntryPoint class MyPageActivity : AppCompatActivity() { + private val viewModel: MyPageViewModel by viewModels() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() @@ -30,7 +36,7 @@ class MyPageActivity : AppCompatActivity() { MyPageScreen( navigateToAllBookmarkContents = { val intent = BookmarkContentActivity.newIntent(this) - startActivity(intent) + bookmarkContentLauncher.launch(intent) }, navigateToContent = { contentId: Long -> Timber.d("마이페이지 북마크 콘텐츠 클릭(contentId=$contentId)") @@ -63,11 +69,27 @@ class MyPageActivity : AppCompatActivity() { startActivity(intent) finish() }, + viewModel = viewModel, ) } } } + private val bookmarkContentLauncher = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult + + val changed = + result.data?.getBooleanExtra( + "BOOKMARK_CONTENT_HAS_BOOKMARK_CHANGES_FLAG", + false, + ) ?: false + + if (changed) { + viewModel.loadBookmarkContents() + } + } + companion object { fun newIntent(context: Context): Intent = Intent(context, MyPageActivity::class.java) } From fd52d7543dfcacf83aa300ba8e8f86898d20ab4e Mon Sep 17 00:00:00 2001 From: yrsel Date: Tue, 24 Feb 2026 01:36:45 +0900 Subject: [PATCH 07/26] refactor: ktlintformat --- .../on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt | 2 +- .../main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt index af33155ad..c09a804c2 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt @@ -14,7 +14,6 @@ import com.on.turip.ui.common.error.toUiError import com.on.turip.ui.common.paging.PagingLoadMode import com.on.turip.ui.common.paging.PagingState import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -27,6 +26,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import timber.log.Timber +import javax.inject.Inject @HiltViewModel class BookmarkContentViewModel @Inject constructor( diff --git a/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt index 677f2ac6a..3829daed4 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt @@ -19,7 +19,6 @@ import com.on.turip.ui.common.error.toUiError import com.on.turip.ui.compose.mypage.model.InquiryMail import com.on.turip.ui.compose.mypage.util.AppEnvironmentInfoProvider import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -32,6 +31,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import timber.log.Timber +import javax.inject.Inject @HiltViewModel class MyPageViewModel @Inject constructor( From 3b55f900d76978ef5ccd188b8ca0c0720fb7445b Mon Sep 17 00:00:00 2001 From: yrsel Date: Tue, 24 Feb 2026 11:15:49 +0900 Subject: [PATCH 08/26] =?UTF-8?q?refactor:=20=EB=B6=81=EB=A7=88=ED=81=AC?= =?UTF-8?q?=20=EC=BD=98=ED=85=90=EC=B8=A0=20=EC=88=98=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20API=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datasource/BookmarkRemoteDataSource.kt | 3 + .../DefaultBookmarkRemoteDataSource.kt | 6 ++ .../bookmarks/dto/BookmarkCountResponse.kt | 10 ++++ .../repository/DefaultBookmarkRepository.kt | 2 + .../data/bookmarks/service/BookmarkService.kt | 4 ++ .../bookmark/repository/BookmarkRepository.kt | 2 + .../bookmarks/BookmarkContentScreen.kt | 59 +++++++++++-------- .../bookmarks/BookmarkContentUiState.kt | 2 + .../bookmarks/BookmarkContentViewModel.kt | 15 +++++ android/app/src/main/res/values/strings.xml | 1 + 10 files changed, 79 insertions(+), 25 deletions(-) create mode 100644 android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkCountResponse.kt diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/datasource/BookmarkRemoteDataSource.kt b/android/app/src/main/java/com/on/turip/data/bookmarks/datasource/BookmarkRemoteDataSource.kt index a60455a0e..db4bbbd6c 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmarks/datasource/BookmarkRemoteDataSource.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmarks/datasource/BookmarkRemoteDataSource.kt @@ -3,6 +3,7 @@ package com.on.turip.data.bookmarks.datasource import com.on.turip.core.result.TuripResult import com.on.turip.data.bookmarks.dto.BookmarkAddRequest import com.on.turip.data.bookmarks.dto.BookmarkContentsResponse +import com.on.turip.data.bookmarks.dto.BookmarkCountResponse interface BookmarkRemoteDataSource { suspend fun postBookmark(bookmarkAddRequest: BookmarkAddRequest): TuripResult @@ -13,4 +14,6 @@ interface BookmarkRemoteDataSource { size: Int, lastId: Long, ): TuripResult + + suspend fun getBookmarkCount(): TuripResult } diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/datasource/DefaultBookmarkRemoteDataSource.kt b/android/app/src/main/java/com/on/turip/data/bookmarks/datasource/DefaultBookmarkRemoteDataSource.kt index 7424da24d..134d06111 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmarks/datasource/DefaultBookmarkRemoteDataSource.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmarks/datasource/DefaultBookmarkRemoteDataSource.kt @@ -3,6 +3,7 @@ package com.on.turip.data.bookmarks.datasource import com.on.turip.core.result.TuripResult import com.on.turip.data.bookmarks.dto.BookmarkAddRequest import com.on.turip.data.bookmarks.dto.BookmarkContentsResponse +import com.on.turip.data.bookmarks.dto.BookmarkCountResponse import com.on.turip.data.bookmarks.service.BookmarkService import com.on.turip.data.result.safeApiCall import kotlinx.coroutines.Dispatchers @@ -31,4 +32,9 @@ class DefaultBookmarkRemoteDataSource @Inject constructor( withContext(coroutineContext) { safeApiCall { bookmarkService.getBookmarks(size, lastId) } } + + override suspend fun getBookmarkCount(): TuripResult = + withContext(coroutineContext) { + safeApiCall { bookmarkService.getBookmarkCount() } + } } diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkCountResponse.kt b/android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkCountResponse.kt new file mode 100644 index 000000000..dde8d03e9 --- /dev/null +++ b/android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkCountResponse.kt @@ -0,0 +1,10 @@ +package com.on.turip.data.bookmarks.dto + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class BookmarkCountResponse( + @SerialName("count") + val count: Int, +) diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/repository/DefaultBookmarkRepository.kt b/android/app/src/main/java/com/on/turip/data/bookmarks/repository/DefaultBookmarkRepository.kt index 60f137f5b..42bc25c6c 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmarks/repository/DefaultBookmarkRepository.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmarks/repository/DefaultBookmarkRepository.kt @@ -22,4 +22,6 @@ class DefaultBookmarkRepository @Inject constructor( size: Int, lastId: Long, ): TuripResult> = bookmarkRemoteDataSource.getBookmarks(size, lastId).mapCatching { it.toDomain() } + + override suspend fun loadBookmarkCount(): TuripResult = bookmarkRemoteDataSource.getBookmarkCount().mapCatching { it.count } } diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/service/BookmarkService.kt b/android/app/src/main/java/com/on/turip/data/bookmarks/service/BookmarkService.kt index 81bfce88c..b7e7d2164 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmarks/service/BookmarkService.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmarks/service/BookmarkService.kt @@ -2,6 +2,7 @@ package com.on.turip.data.bookmarks.service import com.on.turip.data.bookmarks.dto.BookmarkAddRequest import com.on.turip.data.bookmarks.dto.BookmarkContentsResponse +import com.on.turip.data.bookmarks.dto.BookmarkCountResponse import de.jensklingenberg.ktorfit.http.Body import de.jensklingenberg.ktorfit.http.DELETE import de.jensklingenberg.ktorfit.http.GET @@ -24,4 +25,7 @@ interface BookmarkService { @Query("size") size: Int, @Query("lastId") lastId: Long, ): BookmarkContentsResponse + + @GET("bookmarks/count") + suspend fun getBookmarkCount(): BookmarkCountResponse } diff --git a/android/app/src/main/java/com/on/turip/domain/bookmark/repository/BookmarkRepository.kt b/android/app/src/main/java/com/on/turip/domain/bookmark/repository/BookmarkRepository.kt index 5c34e213c..96bfcb8a7 100644 --- a/android/app/src/main/java/com/on/turip/domain/bookmark/repository/BookmarkRepository.kt +++ b/android/app/src/main/java/com/on/turip/domain/bookmark/repository/BookmarkRepository.kt @@ -13,4 +13,6 @@ interface BookmarkRepository { size: Int, lastId: Long, ): TuripResult> + + suspend fun loadBookmarkCount(): TuripResult } diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt index 60083c46c..e4065beab 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt @@ -140,7 +140,7 @@ private fun BookmarkContentContent( BookmarkContentEmpty() } else { BookmarkContents( - pagingState = uiState.bookmarkContents, + uiState = uiState, onContentClick = onContentClick, onBookmarkClick = onBookmarkClick, loadMore = loadMoreContents, @@ -192,11 +192,12 @@ private fun BookmarkContentEmpty() { @Composable private fun BookmarkContents( - pagingState: PagingState, + uiState: BookmarkContentUiState, onContentClick: (contentId: Long) -> Unit, onBookmarkClick: (contentId: Long) -> Unit, loadMore: () -> Unit, ) { + val pagingState: PagingState = uiState.bookmarkContents val listState = rememberLazyListState() val threshold = 1 val shouldLoadMore by remember { @@ -224,12 +225,15 @@ private fun BookmarkContents( .collect { loadMore() } } + val totalBookmarkCount = + if (uiState.totalBookmarkCount != null) { + stringResource(R.string.bookmark_content_count, uiState.totalBookmarkCount) + } else { + stringResource(R.string.bookmark_content_count_fail) + } + Text( - text = - stringResource( - R.string.bookmark_content_count, - pagingState.items.size, - ), + text = totalBookmarkCount, textAlign = TextAlign.End, style = TuripTheme.typography.info2, color = TuripTheme.colors.gray03, @@ -342,6 +346,7 @@ private fun BookmarkContentEmptyPreview() { isAppending = false, errorUiState = ErrorUiState.None, ), + totalBookmarkCount = null, errorUiState = ErrorUiState.None, ), onRetryClick = {}, @@ -367,6 +372,7 @@ private fun BookmarkContentErrorPreview() { isAppending = false, errorUiState = ErrorUiState.None, ), + totalBookmarkCount = null, errorUiState = ErrorUiState.Network, ), onRetryClick = {}, @@ -408,23 +414,26 @@ private fun BookmarkContentErrorSuccess() { ), ) TuripTheme { - BookmarkContentContent( - uiState = - BookmarkContentUiState( - isLoading = false, - bookmarkContents = - PagingState( - items = contents, - hasNext = false, - isAppending = false, - errorUiState = ErrorUiState.None, - ), - errorUiState = ErrorUiState.None, - ), - onRetryClick = {}, - onContentClick = {}, - onBookmarkClick = {}, - loadMoreContents = {}, - ) + Column { + BookmarkContentContent( + uiState = + BookmarkContentUiState( + isLoading = false, + bookmarkContents = + PagingState( + items = contents, + hasNext = false, + isAppending = false, + errorUiState = ErrorUiState.None, + ), + totalBookmarkCount = 10, + errorUiState = ErrorUiState.None, + ), + onRetryClick = {}, + onContentClick = {}, + onBookmarkClick = {}, + loadMoreContents = {}, + ) + } } } diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiState.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiState.kt index 22cbda516..713da03ff 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiState.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiState.kt @@ -10,6 +10,7 @@ import kotlinx.collections.immutable.persistentListOf data class BookmarkContentUiState( val isLoading: Boolean, val bookmarkContents: PagingState, + val totalBookmarkCount: Int?, val errorUiState: ErrorUiState, ) { val isEmpty: Boolean @@ -26,6 +27,7 @@ data class BookmarkContentUiState( isAppending = false, errorUiState = ErrorUiState.None, ), + totalBookmarkCount = null, errorUiState = ErrorUiState.None, ) } diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt index c09a804c2..6a298c9ef 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt @@ -47,6 +47,15 @@ class BookmarkContentViewModel @Inject constructor( loadBookmarkContents(PagingLoadMode.REFRESH) } + private suspend fun loadBookmarkCount() = + bookmarkRepository + .loadBookmarkCount() + .onSuccess { count: Int -> + _uiState.update { state -> state.copy(totalBookmarkCount = count) } + }.onFailure { + _uiState.update { state -> state.copy(totalBookmarkCount = null) } + } + fun loadMoreContents() { loadBookmarkContents(PagingLoadMode.APPEND) } @@ -86,6 +95,9 @@ class BookmarkContentViewModel @Inject constructor( } } + // 새로고침할 때만 전체 콘텐츠 수 API 호출 + if (loadMode == PagingLoadMode.REFRESH) launch { loadBookmarkCount() } + val lastItemId = when (loadMode) { PagingLoadMode.REFRESH -> { @@ -234,6 +246,9 @@ class BookmarkContentViewModel @Inject constructor( bookmarkRepository .deleteBookmark(contentId) .onSuccess { + _uiState.update { state -> + state.copy(totalBookmarkCount = state.totalBookmarkCount?.minus(1)) + } _uiEffect.send(BookmarkContentUiEffect.BookmarkRemoved) }.onFailure { _uiEffect.send(BookmarkContentUiEffect.ShowBookmarkRemoveFailed) diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index e27d2ed48..f64b1ebe3 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -72,6 +72,7 @@ 컨텐츠를 저장 해보세요! 원하는 동선이 담긴\n여행 일정을 저장해 보세요. 데이터를 불러올 수 없습니다. 잠시 후 재시도 해주세요 + 콘텐츠 - 튜립에 장소를 추가 해보세요! From 748f51471f6e551b7d85fd5c115fce65da8b6871 Mon Sep 17 00:00:00 2001 From: yrsel Date: Tue, 24 Feb 2026 12:09:27 +0900 Subject: [PATCH 09/26] =?UTF-8?q?refactor:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20API=20=EB=82=B4=EB=B6=80=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC,=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EC=96=B4=EB=85=B8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../turip/ui/common/paging/PagingLoadMode.kt | 3 - .../bookmarks/BookmarkContentViewModel.kt | 292 +++++++++--------- 2 files changed, 152 insertions(+), 143 deletions(-) diff --git a/android/app/src/main/java/com/on/turip/ui/common/paging/PagingLoadMode.kt b/android/app/src/main/java/com/on/turip/ui/common/paging/PagingLoadMode.kt index 7b937907d..3690eff72 100644 --- a/android/app/src/main/java/com/on/turip/ui/common/paging/PagingLoadMode.kt +++ b/android/app/src/main/java/com/on/turip/ui/common/paging/PagingLoadMode.kt @@ -1,8 +1,5 @@ package com.on.turip.ui.common.paging -import androidx.compose.runtime.Immutable - -@Immutable enum class PagingLoadMode { REFRESH, APPEND, diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt index 6a298c9ef..eb4b34004 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt @@ -47,6 +47,74 @@ class BookmarkContentViewModel @Inject constructor( loadBookmarkContents(PagingLoadMode.REFRESH) } + fun loadMoreContents() { + loadBookmarkContents(PagingLoadMode.APPEND) + } + + private fun loadBookmarkContents(loadMode: PagingLoadMode) { + viewModelScope.launch { + if (!prepareLoadingState(loadMode)) return@launch + + // 새로고침할 때만 전체 콘텐츠 수 API 호출 + if (loadMode == PagingLoadMode.REFRESH) launch { loadBookmarkCount() } + + val lastItemId: Long = getLastItemId(loadMode) + + bookmarkRepository + .loadBookmarks(PAGE_SIZE, lastItemId) + .onSuccess { result: Page -> + Timber.d("북마크 화면 조회 성공 mode =$loadMode") + applyBookmarkContents(loadMode, result) + }.onFailure { errorType: ErrorType -> + Timber.e("북마크 화면 에러 loadMode = $loadMode") + val uiError: UiError.Global = errorType.toUiError() as UiError.Global + when (loadMode) { + PagingLoadMode.REFRESH -> applyBookmarkContentsRefreshFailure(uiError) + PagingLoadMode.APPEND -> applyBookmarkContentsAppendFailure(uiError) + } + } + } + } + + private fun prepareLoadingState(loadMode: PagingLoadMode): Boolean { + return when (loadMode) { + PagingLoadMode.REFRESH -> { + _uiState.update { state -> + state.copy( + isLoading = true, + errorUiState = ErrorUiState.None, + bookmarkContents = + state.bookmarkContents.copy( + isAppending = false, + errorUiState = ErrorUiState.None, + ), + ) + } + true + } + + PagingLoadMode.APPEND -> { + val pagingState = uiState.value.bookmarkContents + val canAppend = + pagingState.hasNext && pagingState.items.isNotEmpty() && !pagingState.isAppending + if (!canAppend) return false + + _uiState.update { state -> + state.copy( + isLoading = false, + bookmarkContents = + state.bookmarkContents.copy( + isAppending = true, + errorUiState = ErrorUiState.None, + ), + errorUiState = ErrorUiState.None, + ) + } + true + } + } + } + private suspend fun loadBookmarkCount() = bookmarkRepository .loadBookmarkCount() @@ -56,158 +124,102 @@ class BookmarkContentViewModel @Inject constructor( _uiState.update { state -> state.copy(totalBookmarkCount = null) } } - fun loadMoreContents() { - loadBookmarkContents(PagingLoadMode.APPEND) - } + private fun getLastItemId(loadMode: PagingLoadMode) = + when (loadMode) { + PagingLoadMode.REFRESH -> { + 0L + } - private fun loadBookmarkContents(loadMode: PagingLoadMode) { - viewModelScope.launch { - when (loadMode) { - PagingLoadMode.REFRESH -> { - _uiState.update { state -> - state.copy( - isLoading = true, - errorUiState = ErrorUiState.None, - bookmarkContents = - state.bookmarkContents.copy( - isAppending = false, - errorUiState = ErrorUiState.None, - ), - ) - } + PagingLoadMode.APPEND -> { + uiState.value.bookmarkContents.items + .last() + .content.id + } + } + + private fun applyBookmarkContents( + loadMode: PagingLoadMode, + result: Page, + ) { + _uiState.update { state -> + val newItems = + when (loadMode) { + PagingLoadMode.REFRESH -> result.items.toImmutableList() + PagingLoadMode.APPEND -> (state.bookmarkContents.items + result.items).toImmutableList() } + state.copy( + isLoading = false, + bookmarkContents = + PagingState( + items = newItems, + hasNext = result.hasNext, + isAppending = false, + errorUiState = ErrorUiState.None, + ), + errorUiState = ErrorUiState.None, + ) + } + } - PagingLoadMode.APPEND -> { - val pagingState = uiState.value.bookmarkContents - if (!pagingState.hasNext || pagingState.items.isEmpty() || pagingState.isAppending) return@launch - - _uiState.update { state -> - state.copy( - isLoading = false, - bookmarkContents = - state.bookmarkContents.copy( - isAppending = true, - errorUiState = ErrorUiState.None, - ), - errorUiState = ErrorUiState.None, - ) - } + private suspend fun applyBookmarkContentsRefreshFailure(uiError: UiError.Global) { + when (uiError) { + UiError.Global.Network -> { + _uiState.update { + it.copy( + isLoading = false, + errorUiState = ErrorUiState.Network, + ) } } - // 새로고침할 때만 전체 콘텐츠 수 API 호출 - if (loadMode == PagingLoadMode.REFRESH) launch { loadBookmarkCount() } + UiError.Global.Server -> { + _uiState.update { + it.copy( + isLoading = false, + errorUiState = ErrorUiState.Server, + ) + } + } - val lastItemId = - when (loadMode) { - PagingLoadMode.REFRESH -> { - 0L - } + UiError.Global.TokenExpired -> { + _uiState.update { it.copy(isLoading = false) } + _uiEffect.send(BookmarkContentUiEffect.NavigateToLogin) + } + } + } - PagingLoadMode.APPEND -> { - uiState.value.bookmarkContents.items - .last() - .content.id - } + private suspend fun applyBookmarkContentsAppendFailure(uiError: UiError.Global) { + when (uiError) { + UiError.Global.Network -> { + _uiState.update { state -> + state.copy( + bookmarkContents = + state.bookmarkContents.copy( + isAppending = false, + errorUiState = ErrorUiState.Network, + ), + ) } + } - bookmarkRepository - .loadBookmarks(PAGE_SIZE, lastItemId) - .onSuccess { result: Page -> - Timber.d("북마크 화면 조회 성공 mode =$loadMode") - _uiState.update { state -> - val newItems = - when (loadMode) { - PagingLoadMode.REFRESH -> result.items.toImmutableList() - PagingLoadMode.APPEND -> (state.bookmarkContents.items + result.items).toImmutableList() - } - state.copy( - isLoading = false, - bookmarkContents = - PagingState( - items = newItems, - hasNext = result.hasNext, - isAppending = false, - errorUiState = ErrorUiState.None, - ), - errorUiState = ErrorUiState.None, - ) - } - }.onFailure { errorType: ErrorType -> - Timber.e("북마크 화면 에러 loadMode = $loadMode") - when (loadMode) { - PagingLoadMode.REFRESH -> { - val uiError: UiError = errorType.toUiError() - if (uiError is UiError.Global) { - when (uiError) { - UiError.Global.Network -> { - _uiState.update { - it.copy( - isLoading = false, - errorUiState = ErrorUiState.Network, - ) - } - } - - UiError.Global.Server -> { - _uiState.update { - it.copy( - isLoading = false, - errorUiState = ErrorUiState.Server, - ) - } - } - - UiError.Global.TokenExpired -> { - _uiState.update { it.copy(isLoading = false) } - _uiEffect.send(BookmarkContentUiEffect.NavigateToLogin) - } - } - } - } + UiError.Global.Server -> { + _uiState.update { state -> + state.copy( + bookmarkContents = + state.bookmarkContents.copy( + isAppending = false, + errorUiState = ErrorUiState.Server, + ), + ) + } + } - PagingLoadMode.APPEND -> { - val uiError: UiError = errorType.toUiError() - if (uiError is UiError.Global) { - when (uiError) { - UiError.Global.Network -> { - _uiState.update { - it.copy( - bookmarkContents = - it.bookmarkContents.copy( - isAppending = false, - errorUiState = ErrorUiState.Network, - ), - ) - } - } - - UiError.Global.Server -> { - _uiState.update { - it.copy( - bookmarkContents = - it.bookmarkContents.copy( - isAppending = false, - errorUiState = ErrorUiState.Server, - ), - ) - } - } - - UiError.Global.TokenExpired -> { - _uiState.update { - it.copy( - bookmarkContents = - it.bookmarkContents.copy(isAppending = false), - ) - } - _uiEffect.send(BookmarkContentUiEffect.NavigateToLogin) - } - } - } - } - } + UiError.Global.TokenExpired -> { + _uiState.update { state -> + state.copy(bookmarkContents = state.bookmarkContents.copy(isAppending = false)) } + _uiEffect.send(BookmarkContentUiEffect.NavigateToLogin) + } } } From 3d3e99a4591dc14be1b9e082500a5dbd316d776d Mon Sep 17 00:00:00 2001 From: yrsel Date: Tue, 24 Feb 2026 12:24:18 +0900 Subject: [PATCH 10/26] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=EB=9E=98?= =?UTF-8?q?=EB=B9=97=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BookmarkContent Result로 내려주는 변경여부 정보를 savedInstanceState로 저장 - 문의하기 인텐트 로직도 safe 하게 처리하도록 수정 - preview 네이밍 수정 --- .../turip/ui/bookmarks/BookmarkContentActivity.kt | 14 ++++++++++++-- .../ui/compose/bookmarks/BookmarkContentScreen.kt | 2 +- .../compose/bookmarks/BookmarkContentViewModel.kt | 7 ++++--- .../java/com/on/turip/ui/mypage/MyPageActivity.kt | 13 +++++++------ android/app/src/main/res/values/strings.xml | 1 + 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt b/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt index 7b0243c23..d0e3a4bc8 100644 --- a/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt +++ b/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt @@ -20,7 +20,9 @@ class BookmarkContentActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() - + hasBookmarkChanges = + savedInstanceState?.getBoolean(EXTRA_BOOKMARK_CONTENT_HAS_BOOKMARK_CHANGES, false) + ?: false setContent { TuripTheme { BookmarkContentScreen( @@ -49,16 +51,24 @@ class BookmarkContentActivity : AppCompatActivity() { } } + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putBoolean(EXTRA_BOOKMARK_CONTENT_HAS_BOOKMARK_CHANGES, hasBookmarkChanges) + } + override fun finish() { val data = Intent().apply { - putExtra("BOOKMARK_CONTENT_HAS_BOOKMARK_CHANGES_FLAG", hasBookmarkChanges) + putExtra(EXTRA_BOOKMARK_CONTENT_HAS_BOOKMARK_CHANGES, hasBookmarkChanges) } setResult(Activity.RESULT_OK, data) super.finish() } companion object { + const val EXTRA_BOOKMARK_CONTENT_HAS_BOOKMARK_CHANGES = + "com.on.turip.ui.bookmarks.BOOKMARK_CHANGES" + fun newIntent(context: Context): Intent = Intent(context, BookmarkContentActivity::class.java) } } diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt index e4065beab..f9f17d1e1 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt @@ -385,7 +385,7 @@ private fun BookmarkContentErrorPreview() { @Preview(showBackground = true, name = "정상") @Composable -private fun BookmarkContentErrorSuccess() { +private fun BookmarkContentSuccessPreview() { val contents = persistentListOf( BookmarkContent( diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt index eb4b34004..d30c519d6 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt @@ -58,7 +58,7 @@ class BookmarkContentViewModel @Inject constructor( // 새로고침할 때만 전체 콘텐츠 수 API 호출 if (loadMode == PagingLoadMode.REFRESH) launch { loadBookmarkCount() } - val lastItemId: Long = getLastItemId(loadMode) + val lastItemId: Long = getLastItemId(loadMode) ?: return@launch bookmarkRepository .loadBookmarks(PAGE_SIZE, lastItemId) @@ -132,8 +132,9 @@ class BookmarkContentViewModel @Inject constructor( PagingLoadMode.APPEND -> { uiState.value.bookmarkContents.items - .last() - .content.id + .lastOrNull() + ?.content + ?.id } } diff --git a/android/app/src/main/java/com/on/turip/ui/mypage/MyPageActivity.kt b/android/app/src/main/java/com/on/turip/ui/mypage/MyPageActivity.kt index a5d9fc8f6..9d6ed683b 100644 --- a/android/app/src/main/java/com/on/turip/ui/mypage/MyPageActivity.kt +++ b/android/app/src/main/java/com/on/turip/ui/mypage/MyPageActivity.kt @@ -13,6 +13,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.net.toUri import com.on.turip.R import com.on.turip.ui.bookmarks.BookmarkContentActivity +import com.on.turip.ui.bookmarks.BookmarkContentActivity.Companion.EXTRA_BOOKMARK_CONTENT_HAS_BOOKMARK_CHANGES import com.on.turip.ui.common.extensions.safeStartActivityWithToast import com.on.turip.ui.compose.designsystem.theme.TuripTheme import com.on.turip.ui.compose.mypage.MyPageScreen @@ -49,9 +50,11 @@ class MyPageActivity : AppCompatActivity() { "mailto:${InquiryMail.RECIPIENT}?subject=${Uri.encode(InquiryMail.TITLE)}&body=${ Uri.encode(mail.content) }".toUri() - val intent: Intent = Intent(Intent.ACTION_SENDTO).apply { data = uri } - startActivity(intent) + safeStartActivityWithToast( + intent = intent, + errorToastMessage = getString(R.string.all_snackbar_not_found_inquiry_url), + ) }, navigateToPrivacyPolicy = { url: String -> val intent = Intent(Intent.ACTION_VIEW, url.toUri()) @@ -80,10 +83,8 @@ class MyPageActivity : AppCompatActivity() { if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult val changed = - result.data?.getBooleanExtra( - "BOOKMARK_CONTENT_HAS_BOOKMARK_CHANGES_FLAG", - false, - ) ?: false + result.data?.getBooleanExtra(EXTRA_BOOKMARK_CONTENT_HAS_BOOKMARK_CHANGES, false) + ?: false if (changed) { viewModel.loadBookmarkContents() diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index f64b1ebe3..b7d23a835 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -21,6 +21,7 @@ 지도 앱으로 이동에 문제가 발생 했어요 개인 정보 처리 방침 이동에 문제가 발생 했어요 영상 앱으로 이동에 문제가 발생 했어요 + 문의하기 이동에 문제가 발생 했어요 공유 하기 마스코트 From 0c7d1f6df00c4407ac88369f8fc0febea6a084ce Mon Sep 17 00:00:00 2001 From: yrsel Date: Fri, 27 Feb 2026 12:01:41 +0900 Subject: [PATCH 11/26] =?UTF-8?q?refactor:=20=ED=9A=8C=EC=9B=90=ED=83=88?= =?UTF-8?q?=ED=87=B4=20strings=20=EB=9D=84=EC=96=B4=EC=93=B0=EA=B8=B0=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 --- android/app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index b7d23a835..58c2636c1 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -55,7 +55,7 @@ 개인 정보 처리 방침 로그인 로그아웃 - 회원 탈퇴 + 회원탈퇴 정말 로그아웃 하시겠습니까? 로그아웃 취소 From 8df69199d728d83deb7c456f1d86d3777a4fe165 Mon Sep 17 00:00:00 2001 From: yrsel Date: Sat, 28 Feb 2026 13:53:37 +0900 Subject: [PATCH 12/26] =?UTF-8?q?refactor:=20=EB=B6=81=EB=A7=88=ED=81=AC?= =?UTF-8?q?=20=EC=88=98=20=EC=A1=B0=ED=9A=8C=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=EA=B0=92=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../turip/ui/compose/bookmarks/BookmarkContentViewModel.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt index d30c519d6..eea744369 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt @@ -14,6 +14,7 @@ import com.on.turip.ui.common.error.toUiError import com.on.turip.ui.common.paging.PagingLoadMode import com.on.turip.ui.common.paging.PagingState import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -26,7 +27,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import timber.log.Timber -import javax.inject.Inject @HiltViewModel class BookmarkContentViewModel @Inject constructor( @@ -115,7 +115,7 @@ class BookmarkContentViewModel @Inject constructor( } } - private suspend fun loadBookmarkCount() = + private suspend fun loadBookmarkCount() { bookmarkRepository .loadBookmarkCount() .onSuccess { count: Int -> @@ -123,6 +123,7 @@ class BookmarkContentViewModel @Inject constructor( }.onFailure { _uiState.update { state -> state.copy(totalBookmarkCount = null) } } + } private fun getLastItemId(loadMode: PagingLoadMode) = when (loadMode) { From 818d72c1b2e82fe4b192a4e3c930fbfce1a8a9e8 Mon Sep 17 00:00:00 2001 From: yrsel Date: Sat, 28 Feb 2026 14:03:52 +0900 Subject: [PATCH 13/26] =?UTF-8?q?refactor:=20BookmarkContentScreen=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=EB=AA=85=EC=97=90=20on=20?= =?UTF-8?q?=EC=A0=91=EB=91=90=EC=82=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../on/turip/ui/bookmarks/BookmarkContentActivity.kt | 9 ++++----- .../ui/compose/bookmarks/BookmarkContentScreen.kt | 12 ++++++------ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt b/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt index d0e3a4bc8..c166e6350 100644 --- a/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt +++ b/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt @@ -1,6 +1,5 @@ package com.on.turip.ui.bookmarks -import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle @@ -26,10 +25,10 @@ class BookmarkContentActivity : AppCompatActivity() { setContent { TuripTheme { BookmarkContentScreen( - navigateToBack = { + onNavigateToBack = { finish() }, - navigateToLogin = { + onNavigateToLogin = { val intent: Intent = LoginActivity.newIntent(this).apply { flags = @@ -38,7 +37,7 @@ class BookmarkContentActivity : AppCompatActivity() { startActivity(intent) finish() }, - navigateToContent = { contentId: Long -> + onNavigateToContent = { contentId: Long -> val intent: Intent = TripDetailActivity.newIntent(context = this, contentId = contentId) startActivity(intent) @@ -61,7 +60,7 @@ class BookmarkContentActivity : AppCompatActivity() { Intent().apply { putExtra(EXTRA_BOOKMARK_CONTENT_HAS_BOOKMARK_CHANGES, hasBookmarkChanges) } - setResult(Activity.RESULT_OK, data) + setResult(RESULT_OK, data) super.finish() } diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt index f9f17d1e1..d7307a2cc 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt @@ -59,9 +59,9 @@ import kotlinx.coroutines.flow.filter @Composable fun BookmarkContentScreen( - navigateToBack: () -> Unit, - navigateToLogin: () -> Unit, - navigateToContent: (contentId: Long) -> Unit, + onNavigateToBack: () -> Unit, + onNavigateToLogin: () -> Unit, + onNavigateToContent: (contentId: Long) -> Unit, onBookmarkChanged: () -> Unit, viewModel: BookmarkContentViewModel = hiltViewModel(), ) { @@ -74,7 +74,7 @@ fun BookmarkContentScreen( viewModel.uiEffect.collect { uiEffect: BookmarkContentUiEffect -> when (uiEffect) { BookmarkContentUiEffect.NavigateToLogin -> { - navigateToLogin() + onNavigateToLogin() } BookmarkContentUiEffect.BookmarkRemoved -> { @@ -93,7 +93,7 @@ fun BookmarkContentScreen( } Scaffold( - topBar = { BookmarkContentAppBar(onBackClick = navigateToBack) }, + topBar = { BookmarkContentAppBar(onBackClick = onNavigateToBack) }, modifier = Modifier .fillMaxSize() @@ -106,7 +106,7 @@ fun BookmarkContentScreen( BookmarkContentContent( uiState = uiState, onRetryClick = viewModel::refreshBookmarkContents, - onContentClick = navigateToContent, + onContentClick = onNavigateToContent, onBookmarkClick = viewModel::removeBookmark, loadMoreContents = viewModel::loadMoreContents, ) From be984be781b36daac7a100c3c04c06cfa2d54cd4 Mon Sep 17 00:00:00 2001 From: yrsel Date: Sat, 28 Feb 2026 15:39:51 +0900 Subject: [PATCH 14/26] =?UTF-8?q?refactor:=20BookmarkContent=20=EB=82=B4?= =?UTF-8?q?=EB=B6=80=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=93=A4=20?= =?UTF-8?q?=EA=B3=B5=ED=86=B5=EC=97=90=EC=84=9C=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=B6=94?= =?UTF-8?q?=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bookmark/BookmarkContentMetaSection.kt | 138 +++++++++++ .../bookmark/BookmarkContentTitleRow.kt | 72 ++++++ .../component/content/ContentThumbnail.kt | 72 ++++++ .../bookmarks/BookmarkContentScreen.kt | 13 +- .../component/BookmarkContentItem.kt | 225 +++--------------- .../mypage/component/BookmarkedContentItem.kt | 163 +------------ 6 files changed, 333 insertions(+), 350 deletions(-) create mode 100644 android/app/src/main/java/com/on/turip/ui/common/component/bookmark/BookmarkContentMetaSection.kt create mode 100644 android/app/src/main/java/com/on/turip/ui/common/component/bookmark/BookmarkContentTitleRow.kt create mode 100644 android/app/src/main/java/com/on/turip/ui/common/component/content/ContentThumbnail.kt diff --git a/android/app/src/main/java/com/on/turip/ui/common/component/bookmark/BookmarkContentMetaSection.kt b/android/app/src/main/java/com/on/turip/ui/common/component/bookmark/BookmarkContentMetaSection.kt new file mode 100644 index 000000000..751709bac --- /dev/null +++ b/android/app/src/main/java/com/on/turip/ui/common/component/bookmark/BookmarkContentMetaSection.kt @@ -0,0 +1,138 @@ +package com.on.turip.ui.common.component.bookmark + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.on.turip.R +import com.on.turip.domain.bookmark.BookmarkContent +import com.on.turip.domain.content.Content +import com.on.turip.domain.content.video.VideoData +import com.on.turip.domain.creator.Creator +import com.on.turip.domain.region.City +import com.on.turip.domain.trip.TripDuration +import com.on.turip.ui.common.mapper.toUiModel +import com.on.turip.ui.compose.designsystem.theme.TuripTheme + +@Composable +fun BookmarkContentMetaSection( + item: BookmarkContent, + onRemoveBookmark: (contentId: Long) -> Unit, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(TuripTheme.spacing.small), + ) { + Text( + text = + stringResource( + R.string.region_result_video_description, + item.content.creator.channelName, + item.content.videoData.uploadedDate, + ), + style = TuripTheme.typography.info2, + color = TuripTheme.colors.gray03, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(TuripTheme.spacing.small), + ) { + ContentInfoItem( + text = item.tripDuration.toUiModel().toDisplayText(LocalContext.current), + iconPainterRes = R.drawable.ic_calendar, + ) + ContentInfoItem( + text = stringResource(R.string.all_total_place_count, item.tripPlaceCount), + iconPainterRes = R.drawable.ic_place, + ) + } + } + IconButton( + onClick = { onRemoveBookmark(item.content.id) }, + modifier = Modifier.size(48.dp), + ) { + Icon( + painter = painterResource(R.drawable.btn_bookmark_selected), + contentDescription = null, + tint = TuripTheme.colors.primary, + ) + } + } +} + +@Composable +private fun ContentInfoItem( + text: String, + @DrawableRes iconPainterRes: Int, + modifier: Modifier = Modifier, + textColor: Color = TuripTheme.colors.gray03, + iconTint: Color = TuripTheme.colors.gray02, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(TuripTheme.spacing.extraSmall), + modifier = modifier, + ) { + Icon( + painter = painterResource(iconPainterRes), + tint = iconTint, + contentDescription = null, + modifier = Modifier.size(12.dp), + ) + + Text( + text = text, + style = TuripTheme.typography.info2, + color = textColor, + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun BookmarkContentMetaSectionPreview() { + TuripTheme { + val content = + BookmarkContent( + content = + Content( + 1L, + Creator(1L, "채널명", ""), + VideoData("", "", "2026-02-12"), + City(""), + true, + ), + tripDuration = TripDuration(0, 1), + tripPlaceCount = 100, + ) + + BookmarkContentMetaSection( + item = content, + onRemoveBookmark = {}, + modifier = Modifier.padding(TuripTheme.spacing.large), + ) + } +} diff --git a/android/app/src/main/java/com/on/turip/ui/common/component/bookmark/BookmarkContentTitleRow.kt b/android/app/src/main/java/com/on/turip/ui/common/component/bookmark/BookmarkContentTitleRow.kt new file mode 100644 index 000000000..d2bb0f04e --- /dev/null +++ b/android/app/src/main/java/com/on/turip/ui/common/component/bookmark/BookmarkContentTitleRow.kt @@ -0,0 +1,72 @@ +package com.on.turip.ui.common.component.bookmark + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import com.on.turip.ui.compose.designsystem.theme.TuripTheme +import com.on.turip.ui.compose.home.component.RegionChip + +@Composable +fun BookmarkContentTitleRow( + title: String, + modifier: Modifier = Modifier, + trailing: (@Composable () -> Unit)? = null, +) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = title, + style = TuripTheme.typography.title2, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = + Modifier + .weight(1f) + .padding(), + ) + trailing?.invoke() + } +} + +@Preview(showBackground = true, name = "칩 포함") +@Composable +private fun BookmarkContentTitleRowWithTrailingPreview() { + TuripTheme { + BookmarkContentTitleRow( + title = "콘텐츠 제목이 길어질 경우 말줄임 처리 확인용 텍스트입니다", + trailing = { + RegionChip( + regionName = "제주", + modifier = Modifier.padding(start = TuripTheme.spacing.small), + ) + }, + modifier = + Modifier + .fillMaxWidth() + .padding(TuripTheme.spacing.large), + ) + } +} + +@Preview(showBackground = true, name = "타이틀만 존재") +@Composable +private fun BookmarkContentTitleRowWithoutTrailingPreview() { + TuripTheme { + BookmarkContentTitleRow( + title = "타이틀만 있는 경우", + trailing = null, + modifier = + Modifier + .fillMaxWidth() + .padding(TuripTheme.spacing.large), + ) + } +} diff --git a/android/app/src/main/java/com/on/turip/ui/common/component/content/ContentThumbnail.kt b/android/app/src/main/java/com/on/turip/ui/common/component/content/ContentThumbnail.kt new file mode 100644 index 000000000..968dd50da --- /dev/null +++ b/android/app/src/main/java/com/on/turip/ui/common/component/content/ContentThumbnail.kt @@ -0,0 +1,72 @@ +package com.on.turip.ui.common.component.content + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil3.compose.AsyncImage +import coil3.request.ImageRequest +import coil3.request.crossfade +import com.on.turip.R +import com.on.turip.ui.common.TuripUrlConverter +import com.on.turip.ui.compose.designsystem.theme.TuripTheme + +@Composable +fun ContentThumbnail( + imageUrl: String, + modifier: Modifier = Modifier, + contentDescription: String? = null, +) { + val shape = TuripTheme.shape.container + val parsedUrl = TuripUrlConverter.convertVideoThumbnailUrl(imageUrl) + + Box( + modifier = + modifier + .fillMaxWidth() + .aspectRatio(16f / 9f) + .clip(shape) + .border( + width = 1.dp, + color = TuripTheme.colors.gray01, + shape = shape, + ), + ) { + AsyncImage( + model = + ImageRequest + .Builder(LocalContext.current) + .data(parsedUrl) + .crossfade(true) + .build(), + contentDescription = contentDescription, + contentScale = ContentScale.Crop, + modifier = Modifier.matchParentSize(), + error = painterResource(R.drawable.bg_image_placeholder), + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun ContentThumbnailPreview() { + TuripTheme { + ContentThumbnail( + imageUrl = "", + modifier = + Modifier + .background(TuripTheme.colors.primary) + .padding(TuripTheme.spacing.small), + ) + } +} diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt index d7307a2cc..8ab891438 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt @@ -253,7 +253,6 @@ private fun BookmarkContents( LazyColumn( state = listState, contentPadding = PaddingValues(TuripTheme.spacing.medium), - verticalArrangement = Arrangement.spacedBy(TuripTheme.spacing.medium), ) { itemsIndexed( items = pagingState.items, @@ -261,10 +260,20 @@ private fun BookmarkContents( ) { index, content -> BookmarkContentItem( content = content, - showDivider = index != pagingState.items.lastIndex, onContentClick = onContentClick, onRemoveBookmark = onBookmarkClick, ) + + if (index != pagingState.items.lastIndex) { + HorizontalDivider( + modifier = + Modifier + .fillMaxWidth() + .padding(vertical = TuripTheme.spacing.medium), + thickness = 1.dp, + color = TuripTheme.colors.gray01, + ) + } } if (pagingState.isAppending) { diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentItem.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentItem.kt index 60c2c978f..fe621f7cf 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentItem.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentItem.kt @@ -1,55 +1,33 @@ package com.on.turip.ui.compose.bookmarks.component -import androidx.annotation.DrawableRes import androidx.compose.foundation.background -import androidx.compose.foundation.border import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.fillMaxSize 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.wrapContentSize -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import coil3.compose.AsyncImage -import coil3.request.ImageRequest -import coil3.request.crossfade -import com.on.turip.R import com.on.turip.domain.bookmark.BookmarkContent import com.on.turip.domain.content.Content import com.on.turip.domain.content.video.VideoData import com.on.turip.domain.creator.Creator import com.on.turip.domain.region.City import com.on.turip.domain.trip.TripDuration -import com.on.turip.ui.common.TuripUrlConverter -import com.on.turip.ui.common.mapper.toUiModel +import com.on.turip.ui.common.component.bookmark.BookmarkContentMetaSection +import com.on.turip.ui.common.component.bookmark.BookmarkContentTitleRow +import com.on.turip.ui.common.component.content.ContentThumbnail import com.on.turip.ui.compose.designsystem.theme.TuripTheme @Composable fun BookmarkContentItem( content: BookmarkContent, - showDivider: Boolean, onContentClick: (contentId: Long) -> Unit, onRemoveBookmark: (contentId: Long) -> Unit, modifier: Modifier = Modifier, @@ -57,100 +35,34 @@ fun BookmarkContentItem( Column( modifier = modifier + .fillMaxWidth() .clip(TuripTheme.shape.container) + .clickable { onContentClick(content.content.id) } + .background(TuripTheme.colors.white) .padding(TuripTheme.spacing.extraSmall), ) { - Column( - modifier = - Modifier - .fillMaxWidth() - .clickable { onContentClick(content.content.id) }, - ) { - ContentThumbnail( - imageUrl = content.content.videoData.url, - modifier = Modifier.fillMaxWidth(), - ) - - Spacer(modifier = Modifier.height(TuripTheme.spacing.medium)) - - TitleAndChipSection( - title = content.content.videoData.title, - city = content.content.city.name, - ) - - Spacer(modifier = Modifier.height(TuripTheme.spacing.small)) - - ContentInformation( - item = content, - onRemoveBookmark = onRemoveBookmark, - ) - } - - if (showDivider) { - HorizontalDivider( - modifier = Modifier.fillMaxWidth(), - thickness = 1.dp, - color = TuripTheme.colors.gray01, - ) - } - } -} + ContentThumbnail( + imageUrl = content.content.videoData.url, + modifier = Modifier.fillMaxWidth(), + ) -@Composable -private fun ContentThumbnail( - imageUrl: String, - modifier: Modifier = Modifier, - contentDescription: String? = null, -) { - val shape = TuripTheme.shape.container - val parsedUrl = TuripUrlConverter.convertVideoThumbnailUrl(imageUrl) + Spacer(modifier = Modifier.height(TuripTheme.spacing.medium)) - Box( - modifier = - modifier - .fillMaxWidth() - .aspectRatio(16f / 9f) - .clip(shape) - .border( - width = 1.dp, - color = TuripTheme.colors.gray01, - shape = shape, - ), - ) { - AsyncImage( - model = - ImageRequest - .Builder(LocalContext.current) - .data(parsedUrl) - .crossfade(true) - .build(), - contentDescription = contentDescription, - contentScale = ContentScale.Crop, - modifier = Modifier.matchParentSize(), - error = painterResource(R.drawable.bg_image_placeholder), + BookmarkContentTitleRow( + title = content.content.videoData.title, + trailing = { + BookmarkRegionChip( + regionName = content.content.city.name, + modifier = Modifier.padding(horizontal = TuripTheme.spacing.small), + ) + }, ) - } -} -@Composable -private fun TitleAndChipSection( - title: String, - city: String, -) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(TuripTheme.spacing.small), - ) { - Text( - text = title, - style = TuripTheme.typography.title2, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.weight(1f), - ) - BookmarkRegionChip( - regionName = city, - modifier = Modifier.padding(horizontal = TuripTheme.spacing.small), + Spacer(modifier = Modifier.height(TuripTheme.spacing.small)) + + BookmarkContentMetaSection( + item = content, + onRemoveBookmark = onRemoveBookmark, ) } } @@ -181,89 +93,6 @@ private fun BookmarkRegionChip( } } -@Composable -private fun ContentInformation( - item: BookmarkContent, - onRemoveBookmark: (contentId: Long) -> Unit, -) { - Row( - modifier = - Modifier - .fillMaxWidth() - .padding(start = TuripTheme.spacing.extraSmall), - verticalAlignment = Alignment.Top, - ) { - Column( - modifier = Modifier.weight(1f), - verticalArrangement = Arrangement.spacedBy(TuripTheme.spacing.small), - ) { - Text( - text = - stringResource( - R.string.region_result_video_description, - item.content.creator.channelName, - item.content.videoData.uploadedDate, - ), - style = TuripTheme.typography.info2, - color = TuripTheme.colors.gray03, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(TuripTheme.spacing.small), - ) { - ContentInfoItem( - text = item.tripDuration.toUiModel().toDisplayText(LocalContext.current), - iconPainterRes = R.drawable.ic_calendar, - ) - ContentInfoItem( - text = stringResource(R.string.all_total_place_count, item.tripPlaceCount), - iconPainterRes = R.drawable.ic_place, - ) - } - } - IconButton( - onClick = { onRemoveBookmark(item.content.id) }, - modifier = Modifier.size(48.dp), - ) { - Icon( - painter = painterResource(R.drawable.btn_bookmark_selected), - contentDescription = null, - tint = TuripTheme.colors.primary, - ) - } - } -} - -@Composable -private fun ContentInfoItem( - text: String, - @DrawableRes iconPainterRes: Int, - modifier: Modifier = Modifier, - textColor: Color = TuripTheme.colors.gray03, - iconTint: Color = TuripTheme.colors.gray02, -) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(TuripTheme.spacing.extraSmall), - modifier = modifier, - ) { - Icon( - painter = painterResource(iconPainterRes), - tint = iconTint, - contentDescription = null, - modifier = Modifier.size(12.dp), - ) - - Text( - text = text, - style = TuripTheme.typography.info2, - color = textColor, - ) - } -} - @Preview(showBackground = true) @Composable private fun BookmarkContentItemPreview() { @@ -283,10 +112,12 @@ private fun BookmarkContentItemPreview() { ) BookmarkContentItem( content = content, - showDivider = true, onContentClick = {}, onRemoveBookmark = {}, - modifier = Modifier.fillMaxSize(), + modifier = + Modifier + .padding(TuripTheme.spacing.large) + .background(TuripTheme.colors.white), ) } } diff --git a/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentItem.kt b/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentItem.kt index 2c90ae2d2..8fe765d25 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentItem.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentItem.kt @@ -1,46 +1,28 @@ package com.on.turip.ui.compose.mypage.component -import androidx.annotation.DrawableRes +import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.aspectRatio 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.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import coil3.compose.AsyncImage -import coil3.request.ImageRequest -import coil3.request.crossfade -import com.on.turip.R import com.on.turip.domain.bookmark.BookmarkContent import com.on.turip.domain.content.Content import com.on.turip.domain.content.video.VideoData import com.on.turip.domain.creator.Creator import com.on.turip.domain.region.City import com.on.turip.domain.trip.TripDuration -import com.on.turip.ui.common.TuripUrlConverter -import com.on.turip.ui.common.mapper.toUiModel +import com.on.turip.ui.common.component.bookmark.BookmarkContentMetaSection +import com.on.turip.ui.common.component.bookmark.BookmarkContentTitleRow +import com.on.turip.ui.common.component.content.ContentThumbnail import com.on.turip.ui.compose.designsystem.theme.TuripTheme @Composable @@ -53,9 +35,10 @@ fun BookmarkedContentItem( Column( modifier = modifier + .border(1.dp, TuripTheme.colors.border, TuripTheme.shape.container) .clip(TuripTheme.shape.container) .clickable { onContentClick(item.content.id) } - .border(1.dp, TuripTheme.colors.border, TuripTheme.shape.container) + .background(TuripTheme.colors.white) .padding(TuripTheme.spacing.extraSmall), ) { ContentThumbnail( @@ -65,142 +48,17 @@ fun BookmarkedContentItem( Spacer(modifier = Modifier.height(TuripTheme.spacing.medium)) - Text( - text = item.content.videoData.title, - style = TuripTheme.typography.title2, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.padding(horizontal = TuripTheme.spacing.extraSmall), - ) + BookmarkContentTitleRow(title = item.content.videoData.title) Spacer(modifier = Modifier.height(TuripTheme.spacing.small)) - ContentInformation( + BookmarkContentMetaSection( item = item, onRemoveBookmark = onRemoveBookmark, ) } } -@Composable -private fun ContentThumbnail( - imageUrl: String, - modifier: Modifier = Modifier, - contentDescription: String? = null, -) { - val shape = TuripTheme.shape.container - val parsedUrl = TuripUrlConverter.convertVideoThumbnailUrl(imageUrl) - - Box( - modifier = - modifier - .fillMaxWidth() - .aspectRatio(16f / 9f) - .clip(shape) - .border( - width = 1.dp, - color = TuripTheme.colors.gray01, - shape = shape, - ), - ) { - AsyncImage( - model = - ImageRequest - .Builder(LocalContext.current) - .data(parsedUrl) - .crossfade(true) - .build(), - contentDescription = contentDescription, - contentScale = ContentScale.Crop, - modifier = Modifier.matchParentSize(), - error = painterResource(R.drawable.bg_image_placeholder), - ) - } -} - -@Composable -private fun ContentInformation( - item: BookmarkContent, - onRemoveBookmark: (contentId: Long) -> Unit, -) { - Row( - modifier = - Modifier - .fillMaxWidth() - .padding(start = TuripTheme.spacing.extraSmall), - verticalAlignment = Alignment.Top, - ) { - Column( - modifier = Modifier.weight(1f), - verticalArrangement = Arrangement.spacedBy(TuripTheme.spacing.small), - ) { - Text( - text = - stringResource( - R.string.region_result_video_description, - item.content.creator.channelName, - item.content.videoData.uploadedDate, - ), - style = TuripTheme.typography.info2, - color = TuripTheme.colors.gray03, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(TuripTheme.spacing.small), - ) { - ContentInfoItem( - text = item.tripDuration.toUiModel().toDisplayText(LocalContext.current), - iconPainterRes = R.drawable.ic_calendar, - ) - ContentInfoItem( - text = stringResource(R.string.all_total_place_count, item.tripPlaceCount), - iconPainterRes = R.drawable.ic_place, - ) - } - } - IconButton( - onClick = { onRemoveBookmark(item.content.id) }, - modifier = Modifier.size(48.dp), - ) { - Icon( - painter = painterResource(R.drawable.btn_bookmark_selected), - contentDescription = null, - tint = TuripTheme.colors.primary, - ) - } - } -} - -@Composable -private fun ContentInfoItem( - text: String, - @DrawableRes iconPainterRes: Int, - modifier: Modifier = Modifier, - textColor: Color = TuripTheme.colors.gray03, - iconTint: Color = TuripTheme.colors.gray02, -) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(TuripTheme.spacing.extraSmall), - modifier = modifier, - ) { - Icon( - painter = painterResource(iconPainterRes), - tint = iconTint, - contentDescription = null, - modifier = Modifier.size(12.dp), - ) - - Text( - text = text, - style = TuripTheme.typography.info2, - color = textColor, - ) - } -} - @Preview(showBackground = true) @Composable private fun BookmarkedContentItemPreview() { @@ -222,7 +80,10 @@ private fun BookmarkedContentItemPreview() { item = content, onContentClick = {}, onRemoveBookmark = {}, - modifier = Modifier.width(280.dp), + modifier = + Modifier + .width(280.dp) + .padding(TuripTheme.spacing.large), ) } } From 55a5c1d98c47cad363ed7daf64ebe6f92c992744 Mon Sep 17 00:00:00 2001 From: yrsel Date: Sat, 28 Feb 2026 15:43:35 +0900 Subject: [PATCH 15/26] =?UTF-8?q?refactor:=20bookmarks,=20accounts=20?= =?UTF-8?q?=EB=B3=B5=EC=88=98=ED=98=95=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/on/turip/data/account/AccountMapper.kt | 12 ++++++++++++ .../datasource/AccountRemoteDataSource.kt | 4 ++-- .../datasource/DefaultAccountRemoteDataSource.kt | 6 +++--- .../{accounts => account}/dto/MyProfileResponse.kt | 2 +- .../repository/DefaultAccountRepository.kt | 10 +++++----- .../{accounts => account}/service/AccountService.kt | 4 ++-- .../java/com/on/turip/data/accounts/AccountMapper.kt | 12 ------------ .../data/{bookmarks => bookmark}/BookmarkMapper.kt | 8 ++++---- .../datasource/BookmarkRemoteDataSource.kt | 8 ++++---- .../datasource/DefaultBookmarkRemoteDataSource.kt | 10 +++++----- .../dto/BookmarkAddRequest.kt | 2 +- .../dto/BookmarkContentResponse.kt | 2 +- .../dto/BookmarkContentsResponse.kt | 2 +- .../dto/BookmarkCountResponse.kt | 2 +- .../repository/DefaultBookmarkRepository.kt | 8 ++++---- .../service/BookmarkService.kt | 8 ++++---- .../main/java/com/on/turip/di/DataSourceModule.kt | 8 ++++---- .../main/java/com/on/turip/di/RepositoryModule.kt | 6 +++--- .../src/main/java/com/on/turip/di/ServiceModule.kt | 8 ++++---- .../on/turip/domain/{accounts => account}/Account.kt | 2 +- .../{accounts => account}/AccountRepository.kt | 2 +- .../on/turip/domain/{accounts => account}/Role.kt | 2 +- .../on/turip/ui/bookmarks/BookmarkContentActivity.kt | 2 +- .../{bookmarks => bookmark}/BookmarkContentScreen.kt | 6 +++--- .../BookmarkContentUiEffect.kt | 2 +- .../BookmarkContentUiState.kt | 2 +- .../BookmarkContentViewModel.kt | 4 ++-- .../component/BookmarkContentAppBar.kt | 2 +- .../component/BookmarkContentItem.kt | 5 ++--- .../on/turip/ui/compose/mypage/MyPageViewModel.kt | 6 +++--- .../on/turip/ui/compose/mypage/util/MyPageMapper.kt | 2 +- 31 files changed, 79 insertions(+), 80 deletions(-) create mode 100644 android/app/src/main/java/com/on/turip/data/account/AccountMapper.kt rename android/app/src/main/java/com/on/turip/data/{accounts => account}/datasource/AccountRemoteDataSource.kt (59%) rename android/app/src/main/java/com/on/turip/data/{accounts => account}/datasource/DefaultAccountRemoteDataSource.kt (70%) rename android/app/src/main/java/com/on/turip/data/{accounts => account}/dto/MyProfileResponse.kt (87%) rename android/app/src/main/java/com/on/turip/data/{accounts => account}/repository/DefaultAccountRepository.kt (60%) rename android/app/src/main/java/com/on/turip/data/{accounts => account}/service/AccountService.kt (59%) delete mode 100644 android/app/src/main/java/com/on/turip/data/accounts/AccountMapper.kt rename android/app/src/main/java/com/on/turip/data/{bookmarks => bookmark}/BookmarkMapper.kt (72%) rename android/app/src/main/java/com/on/turip/data/{bookmarks => bookmark}/datasource/BookmarkRemoteDataSource.kt (65%) rename android/app/src/main/java/com/on/turip/data/{bookmarks => bookmark}/datasource/DefaultBookmarkRemoteDataSource.kt (81%) rename android/app/src/main/java/com/on/turip/data/{bookmarks => bookmark}/dto/BookmarkAddRequest.kt (82%) rename android/app/src/main/java/com/on/turip/data/{bookmarks => bookmark}/dto/BookmarkContentResponse.kt (92%) rename android/app/src/main/java/com/on/turip/data/{bookmarks => bookmark}/dto/BookmarkContentsResponse.kt (87%) rename android/app/src/main/java/com/on/turip/data/{bookmarks => bookmark}/dto/BookmarkCountResponse.kt (81%) rename android/app/src/main/java/com/on/turip/data/{bookmarks => bookmark}/repository/DefaultBookmarkRepository.kt (83%) rename android/app/src/main/java/com/on/turip/data/{bookmarks => bookmark}/service/BookmarkService.kt (76%) rename android/app/src/main/java/com/on/turip/domain/{accounts => account}/Account.kt (70%) rename android/app/src/main/java/com/on/turip/domain/{accounts => account}/AccountRepository.kt (78%) rename android/app/src/main/java/com/on/turip/domain/{accounts => account}/Role.kt (88%) rename android/app/src/main/java/com/on/turip/ui/compose/{bookmarks => bookmark}/BookmarkContentScreen.kt (98%) rename android/app/src/main/java/com/on/turip/ui/compose/{bookmarks => bookmark}/BookmarkContentUiEffect.kt (84%) rename android/app/src/main/java/com/on/turip/ui/compose/{bookmarks => bookmark}/BookmarkContentUiState.kt (96%) rename android/app/src/main/java/com/on/turip/ui/compose/{bookmarks => bookmark}/BookmarkContentViewModel.kt (99%) rename android/app/src/main/java/com/on/turip/ui/compose/{bookmarks => bookmark}/component/BookmarkContentAppBar.kt (96%) rename android/app/src/main/java/com/on/turip/ui/compose/{bookmarks => bookmark}/component/BookmarkContentItem.kt (97%) diff --git a/android/app/src/main/java/com/on/turip/data/account/AccountMapper.kt b/android/app/src/main/java/com/on/turip/data/account/AccountMapper.kt new file mode 100644 index 000000000..c0ad6e7cf --- /dev/null +++ b/android/app/src/main/java/com/on/turip/data/account/AccountMapper.kt @@ -0,0 +1,12 @@ +package com.on.turip.data.account + +import com.on.turip.data.account.dto.MyProfileResponse +import com.on.turip.domain.account.Account +import com.on.turip.domain.account.Role + +fun MyProfileResponse.toDomain(): Account = + Account( + id = id, + nickname = nickname, + role = Role.from(role), + ) diff --git a/android/app/src/main/java/com/on/turip/data/accounts/datasource/AccountRemoteDataSource.kt b/android/app/src/main/java/com/on/turip/data/account/datasource/AccountRemoteDataSource.kt similarity index 59% rename from android/app/src/main/java/com/on/turip/data/accounts/datasource/AccountRemoteDataSource.kt rename to android/app/src/main/java/com/on/turip/data/account/datasource/AccountRemoteDataSource.kt index 86e924419..834cc77dd 100644 --- a/android/app/src/main/java/com/on/turip/data/accounts/datasource/AccountRemoteDataSource.kt +++ b/android/app/src/main/java/com/on/turip/data/account/datasource/AccountRemoteDataSource.kt @@ -1,7 +1,7 @@ -package com.on.turip.data.accounts.datasource +package com.on.turip.data.account.datasource import com.on.turip.core.result.TuripResult -import com.on.turip.data.accounts.dto.MyProfileResponse +import com.on.turip.data.account.dto.MyProfileResponse interface AccountRemoteDataSource { suspend fun getMyProfile(): TuripResult diff --git a/android/app/src/main/java/com/on/turip/data/accounts/datasource/DefaultAccountRemoteDataSource.kt b/android/app/src/main/java/com/on/turip/data/account/datasource/DefaultAccountRemoteDataSource.kt similarity index 70% rename from android/app/src/main/java/com/on/turip/data/accounts/datasource/DefaultAccountRemoteDataSource.kt rename to android/app/src/main/java/com/on/turip/data/account/datasource/DefaultAccountRemoteDataSource.kt index ce4675f27..2e316309e 100644 --- a/android/app/src/main/java/com/on/turip/data/accounts/datasource/DefaultAccountRemoteDataSource.kt +++ b/android/app/src/main/java/com/on/turip/data/account/datasource/DefaultAccountRemoteDataSource.kt @@ -1,8 +1,8 @@ -package com.on.turip.data.accounts.datasource +package com.on.turip.data.account.datasource import com.on.turip.core.result.TuripResult -import com.on.turip.data.accounts.dto.MyProfileResponse -import com.on.turip.data.accounts.service.AccountService +import com.on.turip.data.account.dto.MyProfileResponse +import com.on.turip.data.account.service.AccountService import com.on.turip.data.result.safeApiCall import javax.inject.Inject diff --git a/android/app/src/main/java/com/on/turip/data/accounts/dto/MyProfileResponse.kt b/android/app/src/main/java/com/on/turip/data/account/dto/MyProfileResponse.kt similarity index 87% rename from android/app/src/main/java/com/on/turip/data/accounts/dto/MyProfileResponse.kt rename to android/app/src/main/java/com/on/turip/data/account/dto/MyProfileResponse.kt index 6d72d5375..5dd0dc242 100644 --- a/android/app/src/main/java/com/on/turip/data/accounts/dto/MyProfileResponse.kt +++ b/android/app/src/main/java/com/on/turip/data/account/dto/MyProfileResponse.kt @@ -1,4 +1,4 @@ -package com.on.turip.data.accounts.dto +package com.on.turip.data.account.dto import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/android/app/src/main/java/com/on/turip/data/accounts/repository/DefaultAccountRepository.kt b/android/app/src/main/java/com/on/turip/data/account/repository/DefaultAccountRepository.kt similarity index 60% rename from android/app/src/main/java/com/on/turip/data/accounts/repository/DefaultAccountRepository.kt rename to android/app/src/main/java/com/on/turip/data/account/repository/DefaultAccountRepository.kt index 7001dfd19..e72edd64d 100644 --- a/android/app/src/main/java/com/on/turip/data/accounts/repository/DefaultAccountRepository.kt +++ b/android/app/src/main/java/com/on/turip/data/account/repository/DefaultAccountRepository.kt @@ -1,11 +1,11 @@ -package com.on.turip.data.accounts.repository +package com.on.turip.data.account.repository import com.on.turip.core.result.TuripResult import com.on.turip.core.result.mapCatching -import com.on.turip.data.accounts.datasource.AccountRemoteDataSource -import com.on.turip.data.accounts.toDomain -import com.on.turip.domain.accounts.Account -import com.on.turip.domain.accounts.AccountRepository +import com.on.turip.data.account.datasource.AccountRemoteDataSource +import com.on.turip.data.account.toDomain +import com.on.turip.domain.account.Account +import com.on.turip.domain.account.AccountRepository import javax.inject.Inject class DefaultAccountRepository @Inject constructor( diff --git a/android/app/src/main/java/com/on/turip/data/accounts/service/AccountService.kt b/android/app/src/main/java/com/on/turip/data/account/service/AccountService.kt similarity index 59% rename from android/app/src/main/java/com/on/turip/data/accounts/service/AccountService.kt rename to android/app/src/main/java/com/on/turip/data/account/service/AccountService.kt index 07dc89db9..1e3d70819 100644 --- a/android/app/src/main/java/com/on/turip/data/accounts/service/AccountService.kt +++ b/android/app/src/main/java/com/on/turip/data/account/service/AccountService.kt @@ -1,6 +1,6 @@ -package com.on.turip.data.accounts.service +package com.on.turip.data.account.service -import com.on.turip.data.accounts.dto.MyProfileResponse +import com.on.turip.data.account.dto.MyProfileResponse import de.jensklingenberg.ktorfit.http.GET interface AccountService { diff --git a/android/app/src/main/java/com/on/turip/data/accounts/AccountMapper.kt b/android/app/src/main/java/com/on/turip/data/accounts/AccountMapper.kt deleted file mode 100644 index 6c4c6fec2..000000000 --- a/android/app/src/main/java/com/on/turip/data/accounts/AccountMapper.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.on.turip.data.accounts - -import com.on.turip.data.accounts.dto.MyProfileResponse -import com.on.turip.domain.accounts.Account -import com.on.turip.domain.accounts.Role - -fun MyProfileResponse.toDomain(): Account = - Account( - id = id, - nickname = nickname, - role = Role.from(role), - ) diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/BookmarkMapper.kt b/android/app/src/main/java/com/on/turip/data/bookmark/BookmarkMapper.kt similarity index 72% rename from android/app/src/main/java/com/on/turip/data/bookmarks/BookmarkMapper.kt rename to android/app/src/main/java/com/on/turip/data/bookmark/BookmarkMapper.kt index bdc7fb284..31f0d7eae 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmarks/BookmarkMapper.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmark/BookmarkMapper.kt @@ -1,8 +1,8 @@ -package com.on.turip.data.bookmarks +package com.on.turip.data.bookmark -import com.on.turip.data.bookmarks.dto.BookmarkAddRequest -import com.on.turip.data.bookmarks.dto.BookmarkContentResponse -import com.on.turip.data.bookmarks.dto.BookmarkContentsResponse +import com.on.turip.data.bookmark.dto.BookmarkAddRequest +import com.on.turip.data.bookmark.dto.BookmarkContentResponse +import com.on.turip.data.bookmark.dto.BookmarkContentsResponse import com.on.turip.data.content.toDomain import com.on.turip.domain.bookmark.BookmarkContent import com.on.turip.domain.common.paging.Page diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/datasource/BookmarkRemoteDataSource.kt b/android/app/src/main/java/com/on/turip/data/bookmark/datasource/BookmarkRemoteDataSource.kt similarity index 65% rename from android/app/src/main/java/com/on/turip/data/bookmarks/datasource/BookmarkRemoteDataSource.kt rename to android/app/src/main/java/com/on/turip/data/bookmark/datasource/BookmarkRemoteDataSource.kt index db4bbbd6c..f6da52cc7 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmarks/datasource/BookmarkRemoteDataSource.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmark/datasource/BookmarkRemoteDataSource.kt @@ -1,9 +1,9 @@ -package com.on.turip.data.bookmarks.datasource +package com.on.turip.data.bookmark.datasource import com.on.turip.core.result.TuripResult -import com.on.turip.data.bookmarks.dto.BookmarkAddRequest -import com.on.turip.data.bookmarks.dto.BookmarkContentsResponse -import com.on.turip.data.bookmarks.dto.BookmarkCountResponse +import com.on.turip.data.bookmark.dto.BookmarkAddRequest +import com.on.turip.data.bookmark.dto.BookmarkContentsResponse +import com.on.turip.data.bookmark.dto.BookmarkCountResponse interface BookmarkRemoteDataSource { suspend fun postBookmark(bookmarkAddRequest: BookmarkAddRequest): TuripResult diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/datasource/DefaultBookmarkRemoteDataSource.kt b/android/app/src/main/java/com/on/turip/data/bookmark/datasource/DefaultBookmarkRemoteDataSource.kt similarity index 81% rename from android/app/src/main/java/com/on/turip/data/bookmarks/datasource/DefaultBookmarkRemoteDataSource.kt rename to android/app/src/main/java/com/on/turip/data/bookmark/datasource/DefaultBookmarkRemoteDataSource.kt index 134d06111..ebc05706e 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmarks/datasource/DefaultBookmarkRemoteDataSource.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmark/datasource/DefaultBookmarkRemoteDataSource.kt @@ -1,10 +1,10 @@ -package com.on.turip.data.bookmarks.datasource +package com.on.turip.data.bookmark.datasource import com.on.turip.core.result.TuripResult -import com.on.turip.data.bookmarks.dto.BookmarkAddRequest -import com.on.turip.data.bookmarks.dto.BookmarkContentsResponse -import com.on.turip.data.bookmarks.dto.BookmarkCountResponse -import com.on.turip.data.bookmarks.service.BookmarkService +import com.on.turip.data.bookmark.dto.BookmarkAddRequest +import com.on.turip.data.bookmark.dto.BookmarkContentsResponse +import com.on.turip.data.bookmark.dto.BookmarkCountResponse +import com.on.turip.data.bookmark.service.BookmarkService import com.on.turip.data.result.safeApiCall import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkAddRequest.kt b/android/app/src/main/java/com/on/turip/data/bookmark/dto/BookmarkAddRequest.kt similarity index 82% rename from android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkAddRequest.kt rename to android/app/src/main/java/com/on/turip/data/bookmark/dto/BookmarkAddRequest.kt index a49882d66..5a6680200 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkAddRequest.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmark/dto/BookmarkAddRequest.kt @@ -1,4 +1,4 @@ -package com.on.turip.data.bookmarks.dto +package com.on.turip.data.bookmark.dto import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkContentResponse.kt b/android/app/src/main/java/com/on/turip/data/bookmark/dto/BookmarkContentResponse.kt similarity index 92% rename from android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkContentResponse.kt rename to android/app/src/main/java/com/on/turip/data/bookmark/dto/BookmarkContentResponse.kt index b46a6b114..1a43db99a 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkContentResponse.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmark/dto/BookmarkContentResponse.kt @@ -1,4 +1,4 @@ -package com.on.turip.data.bookmarks.dto +package com.on.turip.data.bookmark.dto import com.on.turip.data.content.dto.ContentResponse import com.on.turip.data.content.dto.TripDurationInformationResponse diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkContentsResponse.kt b/android/app/src/main/java/com/on/turip/data/bookmark/dto/BookmarkContentsResponse.kt similarity index 87% rename from android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkContentsResponse.kt rename to android/app/src/main/java/com/on/turip/data/bookmark/dto/BookmarkContentsResponse.kt index 139ca4aa9..fa86764f0 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkContentsResponse.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmark/dto/BookmarkContentsResponse.kt @@ -1,4 +1,4 @@ -package com.on.turip.data.bookmarks.dto +package com.on.turip.data.bookmark.dto import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkCountResponse.kt b/android/app/src/main/java/com/on/turip/data/bookmark/dto/BookmarkCountResponse.kt similarity index 81% rename from android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkCountResponse.kt rename to android/app/src/main/java/com/on/turip/data/bookmark/dto/BookmarkCountResponse.kt index dde8d03e9..2a76a8ba8 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmarks/dto/BookmarkCountResponse.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmark/dto/BookmarkCountResponse.kt @@ -1,4 +1,4 @@ -package com.on.turip.data.bookmarks.dto +package com.on.turip.data.bookmark.dto import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/repository/DefaultBookmarkRepository.kt b/android/app/src/main/java/com/on/turip/data/bookmark/repository/DefaultBookmarkRepository.kt similarity index 83% rename from android/app/src/main/java/com/on/turip/data/bookmarks/repository/DefaultBookmarkRepository.kt rename to android/app/src/main/java/com/on/turip/data/bookmark/repository/DefaultBookmarkRepository.kt index 42bc25c6c..63e7686ee 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmarks/repository/DefaultBookmarkRepository.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmark/repository/DefaultBookmarkRepository.kt @@ -1,10 +1,10 @@ -package com.on.turip.data.bookmarks.repository +package com.on.turip.data.bookmark.repository import com.on.turip.core.result.TuripResult import com.on.turip.core.result.mapCatching -import com.on.turip.data.bookmarks.datasource.BookmarkRemoteDataSource -import com.on.turip.data.bookmarks.toDomain -import com.on.turip.data.bookmarks.toRequestDto +import com.on.turip.data.bookmark.datasource.BookmarkRemoteDataSource +import com.on.turip.data.bookmark.toDomain +import com.on.turip.data.bookmark.toRequestDto import com.on.turip.domain.bookmark.BookmarkContent import com.on.turip.domain.bookmark.repository.BookmarkRepository import com.on.turip.domain.common.paging.Page diff --git a/android/app/src/main/java/com/on/turip/data/bookmarks/service/BookmarkService.kt b/android/app/src/main/java/com/on/turip/data/bookmark/service/BookmarkService.kt similarity index 76% rename from android/app/src/main/java/com/on/turip/data/bookmarks/service/BookmarkService.kt rename to android/app/src/main/java/com/on/turip/data/bookmark/service/BookmarkService.kt index b7e7d2164..d780c69b2 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmarks/service/BookmarkService.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmark/service/BookmarkService.kt @@ -1,8 +1,8 @@ -package com.on.turip.data.bookmarks.service +package com.on.turip.data.bookmark.service -import com.on.turip.data.bookmarks.dto.BookmarkAddRequest -import com.on.turip.data.bookmarks.dto.BookmarkContentsResponse -import com.on.turip.data.bookmarks.dto.BookmarkCountResponse +import com.on.turip.data.bookmark.dto.BookmarkAddRequest +import com.on.turip.data.bookmark.dto.BookmarkContentsResponse +import com.on.turip.data.bookmark.dto.BookmarkCountResponse import de.jensklingenberg.ktorfit.http.Body import de.jensklingenberg.ktorfit.http.DELETE import de.jensklingenberg.ktorfit.http.GET diff --git a/android/app/src/main/java/com/on/turip/di/DataSourceModule.kt b/android/app/src/main/java/com/on/turip/di/DataSourceModule.kt index 732494988..5450b0b9e 100644 --- a/android/app/src/main/java/com/on/turip/di/DataSourceModule.kt +++ b/android/app/src/main/java/com/on/turip/di/DataSourceModule.kt @@ -1,9 +1,9 @@ package com.on.turip.di -import com.on.turip.data.accounts.datasource.AccountRemoteDataSource -import com.on.turip.data.accounts.datasource.DefaultAccountRemoteDataSource -import com.on.turip.data.bookmarks.datasource.BookmarkRemoteDataSource -import com.on.turip.data.bookmarks.datasource.DefaultBookmarkRemoteDataSource +import com.on.turip.data.account.datasource.AccountRemoteDataSource +import com.on.turip.data.account.datasource.DefaultAccountRemoteDataSource +import com.on.turip.data.bookmark.datasource.BookmarkRemoteDataSource +import com.on.turip.data.bookmark.datasource.DefaultBookmarkRemoteDataSource import com.on.turip.data.content.datasource.ContentRemoteDataSource import com.on.turip.data.content.datasource.DefaultContentRemoteDataSource import com.on.turip.data.login.datasource.AuthDataSource diff --git a/android/app/src/main/java/com/on/turip/di/RepositoryModule.kt b/android/app/src/main/java/com/on/turip/di/RepositoryModule.kt index 1735a5499..6a045ca08 100644 --- a/android/app/src/main/java/com/on/turip/di/RepositoryModule.kt +++ b/android/app/src/main/java/com/on/turip/di/RepositoryModule.kt @@ -1,7 +1,7 @@ package com.on.turip.di -import com.on.turip.data.accounts.repository.DefaultAccountRepository -import com.on.turip.data.bookmarks.repository.DefaultBookmarkRepository +import com.on.turip.data.account.repository.DefaultAccountRepository +import com.on.turip.data.bookmark.repository.DefaultBookmarkRepository import com.on.turip.data.content.repository.DefaultContentRepository import com.on.turip.data.login.repository.DefaultAuthRepository import com.on.turip.data.login.repository.DefaultGuestRepository @@ -10,7 +10,7 @@ import com.on.turip.data.region.repository.DefaultRegionRepository import com.on.turip.data.searchhistory.repository.DefaultSearchHistoryRepository import com.on.turip.data.turip.repository.DefaultTuripRepository import com.on.turip.data.userstorage.repository.DefaultUserStorageRepository -import com.on.turip.domain.accounts.AccountRepository +import com.on.turip.domain.account.AccountRepository import com.on.turip.domain.bookmark.repository.BookmarkRepository import com.on.turip.domain.content.repository.ContentRepository import com.on.turip.domain.login.AuthRepository diff --git a/android/app/src/main/java/com/on/turip/di/ServiceModule.kt b/android/app/src/main/java/com/on/turip/di/ServiceModule.kt index 27f09152c..8cede2f3c 100644 --- a/android/app/src/main/java/com/on/turip/di/ServiceModule.kt +++ b/android/app/src/main/java/com/on/turip/di/ServiceModule.kt @@ -1,9 +1,9 @@ package com.on.turip.di -import com.on.turip.data.accounts.service.AccountService -import com.on.turip.data.accounts.service.createAccountService -import com.on.turip.data.bookmarks.service.BookmarkService -import com.on.turip.data.bookmarks.service.createBookmarkService +import com.on.turip.data.account.service.AccountService +import com.on.turip.data.account.service.createAccountService +import com.on.turip.data.bookmark.service.BookmarkService +import com.on.turip.data.bookmark.service.createBookmarkService import com.on.turip.data.content.service.ContentService import com.on.turip.data.content.service.createContentService import com.on.turip.data.login.service.AuthService diff --git a/android/app/src/main/java/com/on/turip/domain/accounts/Account.kt b/android/app/src/main/java/com/on/turip/domain/account/Account.kt similarity index 70% rename from android/app/src/main/java/com/on/turip/domain/accounts/Account.kt rename to android/app/src/main/java/com/on/turip/domain/account/Account.kt index bdb9c1306..106d1cea6 100644 --- a/android/app/src/main/java/com/on/turip/domain/accounts/Account.kt +++ b/android/app/src/main/java/com/on/turip/domain/account/Account.kt @@ -1,4 +1,4 @@ -package com.on.turip.domain.accounts +package com.on.turip.domain.account data class Account( val id: Long, diff --git a/android/app/src/main/java/com/on/turip/domain/accounts/AccountRepository.kt b/android/app/src/main/java/com/on/turip/domain/account/AccountRepository.kt similarity index 78% rename from android/app/src/main/java/com/on/turip/domain/accounts/AccountRepository.kt rename to android/app/src/main/java/com/on/turip/domain/account/AccountRepository.kt index cf314859a..021ccbe4c 100644 --- a/android/app/src/main/java/com/on/turip/domain/accounts/AccountRepository.kt +++ b/android/app/src/main/java/com/on/turip/domain/account/AccountRepository.kt @@ -1,4 +1,4 @@ -package com.on.turip.domain.accounts +package com.on.turip.domain.account import com.on.turip.core.result.TuripResult diff --git a/android/app/src/main/java/com/on/turip/domain/accounts/Role.kt b/android/app/src/main/java/com/on/turip/domain/account/Role.kt similarity index 88% rename from android/app/src/main/java/com/on/turip/domain/accounts/Role.kt rename to android/app/src/main/java/com/on/turip/domain/account/Role.kt index 49e5bca64..b30725564 100644 --- a/android/app/src/main/java/com/on/turip/domain/accounts/Role.kt +++ b/android/app/src/main/java/com/on/turip/domain/account/Role.kt @@ -1,4 +1,4 @@ -package com.on.turip.domain.accounts +package com.on.turip.domain.account enum class Role( private val tag: String, diff --git a/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt b/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt index c166e6350..eed111616 100644 --- a/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt +++ b/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt @@ -6,7 +6,7 @@ import android.os.Bundle import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity -import com.on.turip.ui.compose.bookmarks.BookmarkContentScreen +import com.on.turip.ui.compose.bookmark.BookmarkContentScreen import com.on.turip.ui.compose.designsystem.theme.TuripTheme import com.on.turip.ui.login.LoginActivity import com.on.turip.ui.trip.TripDetailActivity diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentScreen.kt similarity index 98% rename from android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt rename to android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentScreen.kt index 8ab891438..f618c6e95 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentScreen.kt @@ -1,4 +1,4 @@ -package com.on.turip.ui.compose.bookmarks +package com.on.turip.ui.compose.bookmark import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -49,8 +49,8 @@ import com.on.turip.domain.trip.TripDuration import com.on.turip.ui.common.error.ErrorUiState import com.on.turip.ui.common.extensions.showSnackbarWithAction import com.on.turip.ui.common.paging.PagingState -import com.on.turip.ui.compose.bookmarks.component.BookmarkContentAppBar -import com.on.turip.ui.compose.bookmarks.component.BookmarkContentItem +import com.on.turip.ui.compose.bookmark.component.BookmarkContentAppBar +import com.on.turip.ui.compose.bookmark.component.BookmarkContentItem import com.on.turip.ui.compose.designsystem.component.ErrorScreen import com.on.turip.ui.compose.designsystem.theme.TuripTheme import kotlinx.collections.immutable.persistentListOf diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiEffect.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentUiEffect.kt similarity index 84% rename from android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiEffect.kt rename to android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentUiEffect.kt index b489dd9cb..1f599385d 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiEffect.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentUiEffect.kt @@ -1,4 +1,4 @@ -package com.on.turip.ui.compose.bookmarks +package com.on.turip.ui.compose.bookmark sealed interface BookmarkContentUiEffect { data object NavigateToLogin : BookmarkContentUiEffect diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiState.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentUiState.kt similarity index 96% rename from android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiState.kt rename to android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentUiState.kt index 713da03ff..7df2f4bd1 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentUiState.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentUiState.kt @@ -1,4 +1,4 @@ -package com.on.turip.ui.compose.bookmarks +package com.on.turip.ui.compose.bookmark import androidx.compose.runtime.Immutable import com.on.turip.domain.bookmark.BookmarkContent diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentViewModel.kt similarity index 99% rename from android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt rename to android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentViewModel.kt index eea744369..5a1fa29f6 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/BookmarkContentViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentViewModel.kt @@ -1,4 +1,4 @@ -package com.on.turip.ui.compose.bookmarks +package com.on.turip.ui.compose.bookmark import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -14,7 +14,6 @@ import com.on.turip.ui.common.error.toUiError import com.on.turip.ui.common.paging.PagingLoadMode import com.on.turip.ui.common.paging.PagingState import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -27,6 +26,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import timber.log.Timber +import javax.inject.Inject @HiltViewModel class BookmarkContentViewModel @Inject constructor( diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentAppBar.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentAppBar.kt similarity index 96% rename from android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentAppBar.kt rename to android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentAppBar.kt index 21fca7e16..7949eb78a 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentAppBar.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentAppBar.kt @@ -1,4 +1,4 @@ -package com.on.turip.ui.compose.bookmarks.component +package com.on.turip.ui.compose.bookmark.component import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxWidth diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentItem.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentItem.kt similarity index 97% rename from android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentItem.kt rename to android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentItem.kt index fe621f7cf..b8c586e51 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmarks/component/BookmarkContentItem.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentItem.kt @@ -1,4 +1,4 @@ -package com.on.turip.ui.compose.bookmarks.component +package com.on.turip.ui.compose.bookmark.component import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -79,8 +79,7 @@ private fun BookmarkRegionChip( .background( color = TuripTheme.colors.chipBackground, shape = TuripTheme.shape.chip, - ) - .padding( + ).padding( horizontal = TuripTheme.spacing.medium, vertical = TuripTheme.spacing.extraSmall, ), diff --git a/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt index 26de76286..dd837c277 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt @@ -5,8 +5,8 @@ import androidx.lifecycle.viewModelScope import com.on.turip.core.result.ErrorType import com.on.turip.core.result.onFailure import com.on.turip.core.result.onSuccess -import com.on.turip.domain.accounts.Account -import com.on.turip.domain.accounts.AccountRepository +import com.on.turip.domain.account.Account +import com.on.turip.domain.account.AccountRepository import com.on.turip.domain.bookmark.BookmarkContent import com.on.turip.domain.bookmark.repository.BookmarkRepository import com.on.turip.domain.common.paging.Page @@ -20,6 +20,7 @@ import com.on.turip.ui.compose.mypage.model.InquiryMail import com.on.turip.ui.compose.mypage.util.AppEnvironmentInfoProvider import com.on.turip.ui.compose.mypage.util.toUiModel import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -32,7 +33,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import timber.log.Timber -import javax.inject.Inject @HiltViewModel class MyPageViewModel @Inject constructor( diff --git a/android/app/src/main/java/com/on/turip/ui/compose/mypage/util/MyPageMapper.kt b/android/app/src/main/java/com/on/turip/ui/compose/mypage/util/MyPageMapper.kt index 9bfa76204..74c8e5085 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/mypage/util/MyPageMapper.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/mypage/util/MyPageMapper.kt @@ -1,6 +1,6 @@ package com.on.turip.ui.compose.mypage.util -import com.on.turip.domain.accounts.Account +import com.on.turip.domain.account.Account import com.on.turip.ui.compose.mypage.model.ProfileModel fun Account.toUiModel(): ProfileModel = From a944888f2e0ba0eff66bab3cb86f70cc504c830d Mon Sep 17 00:00:00 2001 From: yrsel Date: Sat, 28 Feb 2026 15:57:06 +0900 Subject: [PATCH 16/26] =?UTF-8?q?refactor:=20=EB=B6=81=EB=A7=88=ED=81=AC?= =?UTF-8?q?=20=EC=BD=98=ED=85=90=EC=B8=A0=20=EB=AA=A9=EB=A1=9D=20scaffold?= =?UTF-8?q?=EC=97=90=20snackbarhost=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/on/turip/ui/compose/bookmark/BookmarkContentScreen.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentScreen.kt index f618c6e95..efa74c96f 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentScreen.kt @@ -99,6 +99,7 @@ fun BookmarkContentScreen( .fillMaxSize() .background(TuripTheme.colors.white) .systemBarsPadding(), + snackbarHost = { TuripSnackbar(snackbarHostState = snackbarHostState) }, ) { innerPadding -> Column( modifier = Modifier.padding(innerPadding), From 2565a78cd0816b98b34f0fafc54360428a843d60 Mon Sep 17 00:00:00 2001 From: yrsel Date: Sat, 28 Feb 2026 15:58:54 +0900 Subject: [PATCH 17/26] =?UTF-8?q?refactor:=20=EB=B6=81=EB=A7=88=ED=81=AC?= =?UTF-8?q?=20=EC=BD=98=ED=85=90=EC=B8=A0=20=EB=AA=A9=EB=A1=9D=20modifier?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compose/bookmark/BookmarkContentScreen.kt | 189 +++++++++--------- 1 file changed, 96 insertions(+), 93 deletions(-) diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentScreen.kt index efa74c96f..b7550a616 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentScreen.kt @@ -52,6 +52,7 @@ import com.on.turip.ui.common.paging.PagingState import com.on.turip.ui.compose.bookmark.component.BookmarkContentAppBar import com.on.turip.ui.compose.bookmark.component.BookmarkContentItem import com.on.turip.ui.compose.designsystem.component.ErrorScreen +import com.on.turip.ui.compose.designsystem.component.TuripSnackbar import com.on.turip.ui.compose.designsystem.theme.TuripTheme import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.distinctUntilChanged @@ -101,17 +102,14 @@ fun BookmarkContentScreen( .systemBarsPadding(), snackbarHost = { TuripSnackbar(snackbarHostState = snackbarHostState) }, ) { innerPadding -> - Column( + BookmarkContentContent( + uiState = uiState, + onRetryClick = viewModel::refreshBookmarkContents, + onContentClick = onNavigateToContent, + onBookmarkClick = viewModel::removeBookmark, + loadMoreContents = viewModel::loadMoreContents, modifier = Modifier.padding(innerPadding), - ) { - BookmarkContentContent( - uiState = uiState, - onRetryClick = viewModel::refreshBookmarkContents, - onContentClick = onNavigateToContent, - onBookmarkClick = viewModel::removeBookmark, - loadMoreContents = viewModel::loadMoreContents, - ) - } + ) } } @@ -122,39 +120,42 @@ private fun BookmarkContentContent( onContentClick: (contentId: Long) -> Unit, onBookmarkClick: (contentId: Long) -> Unit, loadMoreContents: () -> Unit, + modifier: Modifier = Modifier, ) { - when { - uiState.isLoading -> { - BookmarkLoading() - } - - uiState.errorUiState != ErrorUiState.None -> { - ErrorScreen( - errorUiState = uiState.errorUiState, - onRetryClick = onRetryClick, - modifier = Modifier.fillMaxSize(), - ) - } + Column(modifier = modifier.fillMaxSize()) { + when { + uiState.isLoading -> { + BookmarkLoading() + } - else -> { - if (uiState.isEmpty) { - BookmarkContentEmpty() - } else { - BookmarkContents( - uiState = uiState, - onContentClick = onContentClick, - onBookmarkClick = onBookmarkClick, - loadMore = loadMoreContents, + uiState.errorUiState != ErrorUiState.None -> { + ErrorScreen( + errorUiState = uiState.errorUiState, + onRetryClick = onRetryClick, + modifier = Modifier.fillMaxSize(), ) } + + else -> { + if (uiState.isEmpty) { + BookmarkContentEmpty() + } else { + BookmarkContents( + uiState = uiState, + onContentClick = onContentClick, + onBookmarkClick = onBookmarkClick, + loadMore = loadMoreContents, + ) + } + } } } } @Composable -private fun BookmarkLoading() { +private fun BookmarkLoading(modifier: Modifier = Modifier) { Box( - modifier = Modifier.fillMaxSize(), + modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center, ) { CircularProgressIndicator( @@ -165,11 +166,11 @@ private fun BookmarkLoading() { } @Composable -private fun BookmarkContentEmpty() { +private fun BookmarkContentEmpty(modifier: Modifier = Modifier) { Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, - modifier = Modifier.fillMaxSize(), + modifier = modifier.fillMaxSize(), ) { Spacer(modifier = Modifier.height(TuripTheme.spacing.huge)) @@ -197,6 +198,7 @@ private fun BookmarkContents( onContentClick: (contentId: Long) -> Unit, onBookmarkClick: (contentId: Long) -> Unit, loadMore: () -> Unit, + modifier: Modifier = Modifier, ) { val pagingState: PagingState = uiState.bookmarkContents val listState = rememberLazyListState() @@ -233,70 +235,71 @@ private fun BookmarkContents( stringResource(R.string.bookmark_content_count_fail) } - Text( - text = totalBookmarkCount, - textAlign = TextAlign.End, - style = TuripTheme.typography.info2, - color = TuripTheme.colors.gray03, - modifier = - Modifier - .fillMaxWidth() - .padding(vertical = TuripTheme.spacing.medium) - .padding(end = TuripTheme.spacing.large), - ) - - HorizontalDivider( - modifier = Modifier.fillMaxWidth(), - thickness = 5.dp, - color = TuripTheme.colors.gray01, - ) - - LazyColumn( - state = listState, - contentPadding = PaddingValues(TuripTheme.spacing.medium), - ) { - itemsIndexed( - items = pagingState.items, - key = { _, item -> item.content.id }, - ) { index, content -> - BookmarkContentItem( - content = content, - onContentClick = onContentClick, - onRemoveBookmark = onBookmarkClick, - ) + Column(modifier = modifier) { + Text( + text = totalBookmarkCount, + textAlign = TextAlign.End, + style = TuripTheme.typography.info2, + color = TuripTheme.colors.gray03, + modifier = + Modifier + .fillMaxWidth() + .padding(vertical = TuripTheme.spacing.medium) + .padding(end = TuripTheme.spacing.large), + ) + + HorizontalDivider( + modifier = Modifier.fillMaxWidth(), + thickness = 5.dp, + color = TuripTheme.colors.gray01, + ) - if (index != pagingState.items.lastIndex) { - HorizontalDivider( - modifier = - Modifier - .fillMaxWidth() - .padding(vertical = TuripTheme.spacing.medium), - thickness = 1.dp, - color = TuripTheme.colors.gray01, + LazyColumn( + state = listState, + contentPadding = PaddingValues(TuripTheme.spacing.medium), + modifier = Modifier.weight(1f), + ) { + itemsIndexed( + items = pagingState.items, + key = { _, item -> item.content.id }, + ) { index, content -> + BookmarkContentItem( + content = content, + onContentClick = onContentClick, + onRemoveBookmark = onBookmarkClick, ) - } - } - if (pagingState.isAppending) { - item { - Box( - modifier = - Modifier - .fillMaxWidth() - .padding(16.dp), - contentAlignment = Alignment.Center, - ) { - CircularProgressIndicator( - modifier = Modifier.size(24.dp), - color = TuripTheme.colors.black, + if (index != pagingState.items.lastIndex) { + HorizontalDivider( + modifier = + Modifier + .fillMaxWidth() + .padding(vertical = TuripTheme.spacing.medium), + thickness = 1.dp, + color = TuripTheme.colors.gray01, ) } } - } else if (pagingState.errorUiState != ErrorUiState.None) { - item { - LoadMoreError( - onRetryClick = loadMore, - ) + + if (pagingState.isAppending) { + item { + Box( + modifier = + Modifier + .fillMaxWidth() + .padding(TuripTheme.spacing.large), + contentAlignment = Alignment.Center, + ) { + CircularProgressIndicator( + modifier = Modifier.size(24.dp), + color = TuripTheme.colors.black, + ) + } + } + } else if (pagingState.errorUiState != ErrorUiState.None) { + item { + LoadMoreError(onRetryClick = loadMore) + } } } } From e5eb90eeda58d803395c3c8ed8419aca3e548bec Mon Sep 17 00:00:00 2001 From: yrsel Date: Sat, 28 Feb 2026 18:33:25 +0900 Subject: [PATCH 18/26] =?UTF-8?q?refactor:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EC=9D=98=20=EB=B6=81=EB=A7=88=ED=81=AC?= =?UTF-8?q?=EC=99=80=20=EB=B6=81=EB=A7=88=ED=81=AC=20=EB=AA=A9=EB=A1=9D?= =?UTF-8?q?=EC=9D=98=20=EB=B6=81=EB=A7=88=ED=81=AC,=20=EA=B5=AC=EB=B6=84?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8A=94=20=EC=9D=B4=EB=A6=84?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/bookmarks/BookmarkContentActivity.kt | 4 +- ...Screen.kt => BookmarkContentListScreen.kt} | 64 +++++++++---------- .../bookmark/BookmarkContentListUiEffect.kt | 9 +++ ...State.kt => BookmarkContentListUiState.kt} | 6 +- ...del.kt => BookmarkContentListViewModel.kt} | 22 +++---- .../bookmark/BookmarkContentUiEffect.kt | 9 --- ...AppBar.kt => BookmarkContentListAppBar.kt} | 6 +- ...tentItem.kt => BookmarkContentListItem.kt} | 9 +-- .../turip/ui/compose/mypage/MyPageScreen.kt | 4 +- ...ntItem.kt => MyPageBookmarkContentItem.kt} | 6 +- ...ion.kt => MyPageBookmarkContentSection.kt} | 37 +++++++---- .../java/com/on/turip/ui/main/MainActivity.kt | 5 +- 12 files changed, 95 insertions(+), 86 deletions(-) rename android/app/src/main/java/com/on/turip/ui/compose/bookmark/{BookmarkContentScreen.kt => BookmarkContentListScreen.kt} (90%) create mode 100644 android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListUiEffect.kt rename android/app/src/main/java/com/on/turip/ui/compose/bookmark/{BookmarkContentUiState.kt => BookmarkContentListUiState.kt} (89%) rename android/app/src/main/java/com/on/turip/ui/compose/bookmark/{BookmarkContentViewModel.kt => BookmarkContentListViewModel.kt} (92%) delete mode 100644 android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentUiEffect.kt rename android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/{BookmarkContentAppBar.kt => BookmarkContentListAppBar.kt} (92%) rename android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/{BookmarkContentItem.kt => BookmarkContentListItem.kt} (96%) rename android/app/src/main/java/com/on/turip/ui/compose/mypage/component/{BookmarkedContentItem.kt => MyPageBookmarkContentItem.kt} (96%) rename android/app/src/main/java/com/on/turip/ui/compose/mypage/component/{BookmarkedContentSection.kt => MyPageBookmarkContentSection.kt} (91%) diff --git a/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt b/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt index eed111616..309e5a12d 100644 --- a/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt +++ b/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt @@ -6,7 +6,7 @@ import android.os.Bundle import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity -import com.on.turip.ui.compose.bookmark.BookmarkContentScreen +import com.on.turip.ui.compose.bookmark.BookmarkContentListScreen import com.on.turip.ui.compose.designsystem.theme.TuripTheme import com.on.turip.ui.login.LoginActivity import com.on.turip.ui.trip.TripDetailActivity @@ -24,7 +24,7 @@ class BookmarkContentActivity : AppCompatActivity() { ?: false setContent { TuripTheme { - BookmarkContentScreen( + BookmarkContentListScreen( onNavigateToBack = { finish() }, diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt similarity index 90% rename from android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentScreen.kt rename to android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt index b7550a616..3baceebe8 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt @@ -49,8 +49,8 @@ import com.on.turip.domain.trip.TripDuration import com.on.turip.ui.common.error.ErrorUiState import com.on.turip.ui.common.extensions.showSnackbarWithAction import com.on.turip.ui.common.paging.PagingState -import com.on.turip.ui.compose.bookmark.component.BookmarkContentAppBar -import com.on.turip.ui.compose.bookmark.component.BookmarkContentItem +import com.on.turip.ui.compose.bookmark.component.BookmarkContentListAppBar +import com.on.turip.ui.compose.bookmark.component.BookmarkContentListItem import com.on.turip.ui.compose.designsystem.component.ErrorScreen import com.on.turip.ui.compose.designsystem.component.TuripSnackbar import com.on.turip.ui.compose.designsystem.theme.TuripTheme @@ -59,12 +59,12 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter @Composable -fun BookmarkContentScreen( +fun BookmarkContentListScreen( onNavigateToBack: () -> Unit, onNavigateToLogin: () -> Unit, onNavigateToContent: (contentId: Long) -> Unit, onBookmarkChanged: () -> Unit, - viewModel: BookmarkContentViewModel = hiltViewModel(), + viewModel: BookmarkContentListViewModel = hiltViewModel(), ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -72,17 +72,17 @@ fun BookmarkContentScreen( val resources = LocalResources.current LaunchedEffect(Unit) { - viewModel.uiEffect.collect { uiEffect: BookmarkContentUiEffect -> + viewModel.uiEffect.collect { uiEffect: BookmarkContentListUiEffect -> when (uiEffect) { - BookmarkContentUiEffect.NavigateToLogin -> { + BookmarkContentListUiEffect.NavigateToLogin -> { onNavigateToLogin() } - BookmarkContentUiEffect.BookmarkRemoved -> { + BookmarkContentListUiEffect.BookmarkRemovedList -> { onBookmarkChanged() } - BookmarkContentUiEffect.ShowBookmarkRemoveFailed -> { + BookmarkContentListUiEffect.ShowBookmarkRemoveFailedList -> { snackbarHostState.showSnackbarWithAction( message = resources.getString(R.string.my_page_snackbar_bookmark_remove_failed), actionLabel = resources.getString(R.string.my_page_snackbar_bookmark_remove_failed_action), @@ -94,7 +94,7 @@ fun BookmarkContentScreen( } Scaffold( - topBar = { BookmarkContentAppBar(onBackClick = onNavigateToBack) }, + topBar = { BookmarkContentListAppBar(onBackClick = onNavigateToBack) }, modifier = Modifier .fillMaxSize() @@ -102,7 +102,7 @@ fun BookmarkContentScreen( .systemBarsPadding(), snackbarHost = { TuripSnackbar(snackbarHostState = snackbarHostState) }, ) { innerPadding -> - BookmarkContentContent( + BookmarkContentListContent( uiState = uiState, onRetryClick = viewModel::refreshBookmarkContents, onContentClick = onNavigateToContent, @@ -114,8 +114,8 @@ fun BookmarkContentScreen( } @Composable -private fun BookmarkContentContent( - uiState: BookmarkContentUiState, +private fun BookmarkContentListContent( + uiState: BookmarkContentListUiState, onRetryClick: () -> Unit, onContentClick: (contentId: Long) -> Unit, onBookmarkClick: (contentId: Long) -> Unit, @@ -125,7 +125,7 @@ private fun BookmarkContentContent( Column(modifier = modifier.fillMaxSize()) { when { uiState.isLoading -> { - BookmarkLoading() + BookmarkContentListLoading() } uiState.errorUiState != ErrorUiState.None -> { @@ -138,9 +138,9 @@ private fun BookmarkContentContent( else -> { if (uiState.isEmpty) { - BookmarkContentEmpty() + BookmarkContentListEmpty() } else { - BookmarkContents( + BookmarkContentList( uiState = uiState, onContentClick = onContentClick, onBookmarkClick = onBookmarkClick, @@ -153,7 +153,7 @@ private fun BookmarkContentContent( } @Composable -private fun BookmarkLoading(modifier: Modifier = Modifier) { +private fun BookmarkContentListLoading(modifier: Modifier = Modifier) { Box( modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center, @@ -166,7 +166,7 @@ private fun BookmarkLoading(modifier: Modifier = Modifier) { } @Composable -private fun BookmarkContentEmpty(modifier: Modifier = Modifier) { +private fun BookmarkContentListEmpty(modifier: Modifier = Modifier) { Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, @@ -193,8 +193,8 @@ private fun BookmarkContentEmpty(modifier: Modifier = Modifier) { } @Composable -private fun BookmarkContents( - uiState: BookmarkContentUiState, +private fun BookmarkContentList( + uiState: BookmarkContentListUiState, onContentClick: (contentId: Long) -> Unit, onBookmarkClick: (contentId: Long) -> Unit, loadMore: () -> Unit, @@ -263,7 +263,7 @@ private fun BookmarkContents( items = pagingState.items, key = { _, item -> item.content.id }, ) { index, content -> - BookmarkContentItem( + BookmarkContentListItem( content = content, onContentClick = onContentClick, onRemoveBookmark = onBookmarkClick, @@ -332,10 +332,10 @@ private fun LoadMoreError(onRetryClick: () -> Unit) { @Preview(showBackground = true, name = "로딩") @Composable -private fun BookmarkContentLoadingPreview() { +private fun BookmarkContentListLoadingPreview() { TuripTheme { - BookmarkContentContent( - uiState = BookmarkContentUiState.Idle, + BookmarkContentListContent( + uiState = BookmarkContentListUiState.Idle, onRetryClick = {}, onContentClick = {}, onBookmarkClick = {}, @@ -346,11 +346,11 @@ private fun BookmarkContentLoadingPreview() { @Preview(showBackground = true, name = "북마크 콘텐츠 비어 있는 경우") @Composable -private fun BookmarkContentEmptyPreview() { +private fun BookmarkContentListEmptyPreview() { TuripTheme { - BookmarkContentContent( + BookmarkContentListContent( uiState = - BookmarkContentUiState( + BookmarkContentListUiState( isLoading = false, bookmarkContents = PagingState( @@ -372,11 +372,11 @@ private fun BookmarkContentEmptyPreview() { @Preview(showBackground = true, name = "에러") @Composable -private fun BookmarkContentErrorPreview() { +private fun BookmarkContentListErrorPreview() { TuripTheme { - BookmarkContentContent( + BookmarkContentListContent( uiState = - BookmarkContentUiState( + BookmarkContentListUiState( isLoading = false, bookmarkContents = PagingState( @@ -398,7 +398,7 @@ private fun BookmarkContentErrorPreview() { @Preview(showBackground = true, name = "정상") @Composable -private fun BookmarkContentSuccessPreview() { +private fun BookmarkContentListSuccessPreview() { val contents = persistentListOf( BookmarkContent( @@ -428,9 +428,9 @@ private fun BookmarkContentSuccessPreview() { ) TuripTheme { Column { - BookmarkContentContent( + BookmarkContentListContent( uiState = - BookmarkContentUiState( + BookmarkContentListUiState( isLoading = false, bookmarkContents = PagingState( diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListUiEffect.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListUiEffect.kt new file mode 100644 index 000000000..4fa665903 --- /dev/null +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListUiEffect.kt @@ -0,0 +1,9 @@ +package com.on.turip.ui.compose.bookmark + +sealed interface BookmarkContentListUiEffect { + data object NavigateToLogin : BookmarkContentListUiEffect + + data object ShowBookmarkRemoveFailedList : BookmarkContentListUiEffect + + data object BookmarkRemovedList : BookmarkContentListUiEffect +} diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentUiState.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListUiState.kt similarity index 89% rename from android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentUiState.kt rename to android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListUiState.kt index 7df2f4bd1..179dc0146 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentUiState.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListUiState.kt @@ -7,7 +7,7 @@ import com.on.turip.ui.common.paging.PagingState import kotlinx.collections.immutable.persistentListOf @Immutable -data class BookmarkContentUiState( +data class BookmarkContentListUiState( val isLoading: Boolean, val bookmarkContents: PagingState, val totalBookmarkCount: Int?, @@ -17,8 +17,8 @@ data class BookmarkContentUiState( get() = !isLoading && bookmarkContents.items.isEmpty() && errorUiState == ErrorUiState.None companion object { - val Idle: BookmarkContentUiState = - BookmarkContentUiState( + val Idle: BookmarkContentListUiState = + BookmarkContentListUiState( isLoading = true, bookmarkContents = PagingState( diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListViewModel.kt similarity index 92% rename from android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentViewModel.kt rename to android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListViewModel.kt index 5a1fa29f6..dc2509f7e 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListViewModel.kt @@ -14,6 +14,7 @@ import com.on.turip.ui.common.error.toUiError import com.on.turip.ui.common.paging.PagingLoadMode import com.on.turip.ui.common.paging.PagingState import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -26,18 +27,17 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import timber.log.Timber -import javax.inject.Inject @HiltViewModel -class BookmarkContentViewModel @Inject constructor( +class BookmarkContentListViewModel @Inject constructor( private val bookmarkRepository: BookmarkRepository, ) : ViewModel() { - private val _uiState: MutableStateFlow = - MutableStateFlow(BookmarkContentUiState.Idle) - val uiState: StateFlow = _uiState.asStateFlow() + private val _uiState: MutableStateFlow = + MutableStateFlow(BookmarkContentListUiState.Idle) + val uiState: StateFlow = _uiState.asStateFlow() - private val _uiEffect: Channel = Channel(Channel.BUFFERED) - val uiEffect: Flow = _uiEffect.receiveAsFlow() + private val _uiEffect: Channel = Channel(Channel.BUFFERED) + val uiEffect: Flow = _uiEffect.receiveAsFlow() init { refreshBookmarkContents() @@ -185,7 +185,7 @@ class BookmarkContentViewModel @Inject constructor( UiError.Global.TokenExpired -> { _uiState.update { it.copy(isLoading = false) } - _uiEffect.send(BookmarkContentUiEffect.NavigateToLogin) + _uiEffect.send(BookmarkContentListUiEffect.NavigateToLogin) } } } @@ -220,7 +220,7 @@ class BookmarkContentViewModel @Inject constructor( _uiState.update { state -> state.copy(bookmarkContents = state.bookmarkContents.copy(isAppending = false)) } - _uiEffect.send(BookmarkContentUiEffect.NavigateToLogin) + _uiEffect.send(BookmarkContentListUiEffect.NavigateToLogin) } } } @@ -263,9 +263,9 @@ class BookmarkContentViewModel @Inject constructor( _uiState.update { state -> state.copy(totalBookmarkCount = state.totalBookmarkCount?.minus(1)) } - _uiEffect.send(BookmarkContentUiEffect.BookmarkRemoved) + _uiEffect.send(BookmarkContentListUiEffect.BookmarkRemovedList) }.onFailure { - _uiEffect.send(BookmarkContentUiEffect.ShowBookmarkRemoveFailed) + _uiEffect.send(BookmarkContentListUiEffect.ShowBookmarkRemoveFailedList) } } finally { // 중복 API 호출 방지 리소스 정리 diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentUiEffect.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentUiEffect.kt deleted file mode 100644 index 1f599385d..000000000 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentUiEffect.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.on.turip.ui.compose.bookmark - -sealed interface BookmarkContentUiEffect { - data object NavigateToLogin : BookmarkContentUiEffect - - data object ShowBookmarkRemoveFailed : BookmarkContentUiEffect - - data object BookmarkRemoved : BookmarkContentUiEffect -} diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentAppBar.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentListAppBar.kt similarity index 92% rename from android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentAppBar.kt rename to android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentListAppBar.kt index 7949eb78a..cb9fbeee8 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentAppBar.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentListAppBar.kt @@ -15,7 +15,7 @@ import com.on.turip.ui.compose.designsystem.component.TuripAppBar import com.on.turip.ui.compose.designsystem.theme.TuripTheme @Composable -fun BookmarkContentAppBar( +fun BookmarkContentListAppBar( onBackClick: () -> Unit, modifier: Modifier = Modifier, ) { @@ -39,9 +39,9 @@ fun BookmarkContentAppBar( @Preview(showBackground = true) @Composable -private fun BookmarkContentAppBarPreview() { +private fun BookmarkContentListAppBarPreview() { TuripTheme { - BookmarkContentAppBar( + BookmarkContentListAppBar( onBackClick = {}, modifier = Modifier.fillMaxWidth(), ) diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentItem.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentListItem.kt similarity index 96% rename from android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentItem.kt rename to android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentListItem.kt index b8c586e51..e936334f1 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentItem.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentListItem.kt @@ -26,7 +26,7 @@ import com.on.turip.ui.common.component.content.ContentThumbnail import com.on.turip.ui.compose.designsystem.theme.TuripTheme @Composable -fun BookmarkContentItem( +fun BookmarkContentListItem( content: BookmarkContent, onContentClick: (contentId: Long) -> Unit, onRemoveBookmark: (contentId: Long) -> Unit, @@ -79,7 +79,8 @@ private fun BookmarkRegionChip( .background( color = TuripTheme.colors.chipBackground, shape = TuripTheme.shape.chip, - ).padding( + ) + .padding( horizontal = TuripTheme.spacing.medium, vertical = TuripTheme.spacing.extraSmall, ), @@ -94,7 +95,7 @@ private fun BookmarkRegionChip( @Preview(showBackground = true) @Composable -private fun BookmarkContentItemPreview() { +private fun BookmarkContentListItemPreview() { TuripTheme { val content = BookmarkContent( @@ -109,7 +110,7 @@ private fun BookmarkContentItemPreview() { tripDuration = TripDuration(1, 2), tripPlaceCount = 2, ) - BookmarkContentItem( + BookmarkContentListItem( content = content, onContentClick = {}, onRemoveBookmark = {}, diff --git a/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageScreen.kt index e44b68c79..5de78a73f 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageScreen.kt @@ -28,8 +28,8 @@ import com.on.turip.ui.common.extensions.showSnackbarWithAction import com.on.turip.ui.compose.designsystem.component.TuripDialog import com.on.turip.ui.compose.designsystem.component.TuripSnackbar import com.on.turip.ui.compose.designsystem.theme.TuripTheme -import com.on.turip.ui.compose.mypage.component.BookmarkedContentSection import com.on.turip.ui.compose.mypage.component.MyPageAppBar +import com.on.turip.ui.compose.mypage.component.MyPageBookmarkContentSection import com.on.turip.ui.compose.mypage.component.MyPageSettingsSection import com.on.turip.ui.compose.mypage.component.ProfileSection import com.on.turip.ui.compose.mypage.model.InquiryMail @@ -200,7 +200,7 @@ private fun MyPageScreenContent( ) } item { - BookmarkedContentSection( + MyPageBookmarkContentSection( state = uiState.bookmarkContentState, onViewAllContentClick = onNavigateToAllBookmarkContents, onContentClick = onNavigateToContent, diff --git a/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentItem.kt b/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/MyPageBookmarkContentItem.kt similarity index 96% rename from android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentItem.kt rename to android/app/src/main/java/com/on/turip/ui/compose/mypage/component/MyPageBookmarkContentItem.kt index 8fe765d25..9d663d3e3 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentItem.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/MyPageBookmarkContentItem.kt @@ -26,7 +26,7 @@ import com.on.turip.ui.common.component.content.ContentThumbnail import com.on.turip.ui.compose.designsystem.theme.TuripTheme @Composable -fun BookmarkedContentItem( +fun MyPageBookmarkContentItem( item: BookmarkContent, onContentClick: (contentId: Long) -> Unit, onRemoveBookmark: (contentId: Long) -> Unit, @@ -61,7 +61,7 @@ fun BookmarkedContentItem( @Preview(showBackground = true) @Composable -private fun BookmarkedContentItemPreview() { +private fun MyPageBookmarkContentItemPreview() { val content = BookmarkContent( content = @@ -76,7 +76,7 @@ private fun BookmarkedContentItemPreview() { tripPlaceCount = 2, ) TuripTheme { - BookmarkedContentItem( + MyPageBookmarkContentItem( item = content, onContentClick = {}, onRemoveBookmark = {}, diff --git a/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentSection.kt b/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/MyPageBookmarkContentSection.kt similarity index 91% rename from android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentSection.kt rename to android/app/src/main/java/com/on/turip/ui/compose/mypage/component/MyPageBookmarkContentSection.kt index da104d17f..117f12d5c 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/BookmarkedContentSection.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/mypage/component/MyPageBookmarkContentSection.kt @@ -44,7 +44,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @Composable -fun BookmarkedContentSection( +fun MyPageBookmarkContentSection( state: MyPageSectionState>, onViewAllContentClick: () -> Unit, onContentClick: (contentId: Long) -> Unit, @@ -81,7 +81,7 @@ fun BookmarkedContentSection( modifier = Modifier.padding(start = TuripTheme.spacing.large), ) { items(items = state.data, key = { it.content.id }) { - BookmarkedContentItem( + MyPageBookmarkContentItem( item = it, onContentClick = onContentClick, onRemoveBookmark = onRemoveBookmark, @@ -228,24 +228,24 @@ private fun EmptyBookmarkedContent() { @Preview(showBackground = true, name = "북마크한 컨텐츠 없음 ") @Composable -private fun BookmarkedContentSectionEmptyPreview() { +private fun MyPageBookmarkContentSectionEmptyPreview() { TuripTheme { - BookmarkedContentSection( + MyPageBookmarkContentSection( state = MyPageSectionState.Success(persistentListOf()), onViewAllContentClick = {}, onContentClick = {}, onRemoveBookmark = {}, onRetry = {}, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.padding(TuripTheme.spacing.large), ) } } @Preview(showBackground = true, name = "컨텐츠 존재") @Composable -private fun BookmarkedContentSectionWithItemsPreview() { +private fun MyPageBookmarkContentSectionWithItemsPreview() { TuripTheme { - BookmarkedContentSection( + MyPageBookmarkContentSection( state = MyPageSectionState.Success( persistentListOf( @@ -271,37 +271,46 @@ private fun BookmarkedContentSectionWithItemsPreview() { onContentClick = {}, onRemoveBookmark = {}, onRetry = {}, - modifier = Modifier.fillMaxWidth(), + modifier = + Modifier + .fillMaxWidth() + .padding(TuripTheme.spacing.large), ) } } @Preview(showBackground = true, name = "로딩 중") @Composable -private fun BookmarkedContentSectionLoadingPreview() { +private fun MyPageBookmarkContentSectionLoadingPreview() { TuripTheme { - BookmarkedContentSection( + MyPageBookmarkContentSection( state = MyPageSectionState.Loading, onViewAllContentClick = {}, onContentClick = {}, onRemoveBookmark = {}, onRetry = {}, - modifier = Modifier.fillMaxWidth(), + modifier = + Modifier + .fillMaxWidth() + .padding(TuripTheme.spacing.large), ) } } @Preview(showBackground = true, name = "에러") @Composable -private fun BookmarkedContentSectionErrorPreview() { +private fun MyPageBookmarkContentSectionErrorPreview() { TuripTheme { - BookmarkedContentSection( + MyPageBookmarkContentSection( state = MyPageSectionState.Error, onViewAllContentClick = {}, onContentClick = {}, onRemoveBookmark = {}, onRetry = {}, - modifier = Modifier.fillMaxWidth(), + modifier = + Modifier + .fillMaxWidth() + .padding(TuripTheme.spacing.large), ) } } diff --git a/android/app/src/main/java/com/on/turip/ui/main/MainActivity.kt b/android/app/src/main/java/com/on/turip/ui/main/MainActivity.kt index 8ee860e24..6fbc7cd97 100644 --- a/android/app/src/main/java/com/on/turip/ui/main/MainActivity.kt +++ b/android/app/src/main/java/com/on/turip/ui/main/MainActivity.kt @@ -16,9 +16,8 @@ import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class MainActivity : BaseActivity() { - override val binding: ActivityMainBinding by lazy { - ActivityMainBinding.inflate(layoutInflater) - } + override val binding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) } + private var backPressedTime: Long = 0L override fun onCreate(savedInstanceState: Bundle?) { From e8f288912905b94a033f1cc0968598b425bf66c7 Mon Sep 17 00:00:00 2001 From: yrsel Date: Sat, 28 Feb 2026 18:44:42 +0900 Subject: [PATCH 19/26] =?UTF-8?q?refactor:=20BookmarkContentList=20?= =?UTF-8?q?=EB=9E=8C=EB=8B=A4=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/bookmarks/BookmarkContentActivity.kt | 2 +- .../bookmark/BookmarkContentListScreen.kt | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt b/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt index 309e5a12d..79346a6cb 100644 --- a/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt +++ b/android/app/src/main/java/com/on/turip/ui/bookmarks/BookmarkContentActivity.kt @@ -25,7 +25,7 @@ class BookmarkContentActivity : AppCompatActivity() { setContent { TuripTheme { BookmarkContentListScreen( - onNavigateToBack = { + onBack = { finish() }, onNavigateToLogin = { diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt index 3baceebe8..1c2f4098e 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt @@ -60,7 +60,7 @@ import kotlinx.coroutines.flow.filter @Composable fun BookmarkContentListScreen( - onNavigateToBack: () -> Unit, + onBack: () -> Unit, onNavigateToLogin: () -> Unit, onNavigateToContent: (contentId: Long) -> Unit, onBookmarkChanged: () -> Unit, @@ -94,7 +94,7 @@ fun BookmarkContentListScreen( } Scaffold( - topBar = { BookmarkContentListAppBar(onBackClick = onNavigateToBack) }, + topBar = { BookmarkContentListAppBar(onBackClick = onBack) }, modifier = Modifier .fillMaxSize() @@ -107,7 +107,7 @@ fun BookmarkContentListScreen( onRetryClick = viewModel::refreshBookmarkContents, onContentClick = onNavigateToContent, onBookmarkClick = viewModel::removeBookmark, - loadMoreContents = viewModel::loadMoreContents, + onLoadMore = viewModel::loadMoreContents, modifier = Modifier.padding(innerPadding), ) } @@ -119,7 +119,7 @@ private fun BookmarkContentListContent( onRetryClick: () -> Unit, onContentClick: (contentId: Long) -> Unit, onBookmarkClick: (contentId: Long) -> Unit, - loadMoreContents: () -> Unit, + onLoadMore: () -> Unit, modifier: Modifier = Modifier, ) { Column(modifier = modifier.fillMaxSize()) { @@ -144,7 +144,7 @@ private fun BookmarkContentListContent( uiState = uiState, onContentClick = onContentClick, onBookmarkClick = onBookmarkClick, - loadMore = loadMoreContents, + onLoadMore = onLoadMore, ) } } @@ -197,7 +197,7 @@ private fun BookmarkContentList( uiState: BookmarkContentListUiState, onContentClick: (contentId: Long) -> Unit, onBookmarkClick: (contentId: Long) -> Unit, - loadMore: () -> Unit, + onLoadMore: () -> Unit, modifier: Modifier = Modifier, ) { val pagingState: PagingState = uiState.bookmarkContents @@ -225,7 +225,7 @@ private fun BookmarkContentList( snapshotFlow { shouldLoadMore } .distinctUntilChanged() .filter { it } - .collect { loadMore() } + .collect { onLoadMore() } } val totalBookmarkCount = @@ -298,7 +298,7 @@ private fun BookmarkContentList( } } else if (pagingState.errorUiState != ErrorUiState.None) { item { - LoadMoreError(onRetryClick = loadMore) + LoadMoreError(onRetryClick = onLoadMore) } } } @@ -339,7 +339,7 @@ private fun BookmarkContentListLoadingPreview() { onRetryClick = {}, onContentClick = {}, onBookmarkClick = {}, - loadMoreContents = { }, + onLoadMore = { }, ) } } @@ -365,7 +365,7 @@ private fun BookmarkContentListEmptyPreview() { onRetryClick = {}, onContentClick = {}, onBookmarkClick = {}, - loadMoreContents = { }, + onLoadMore = { }, ) } } @@ -391,7 +391,7 @@ private fun BookmarkContentListErrorPreview() { onRetryClick = {}, onContentClick = {}, onBookmarkClick = {}, - loadMoreContents = { }, + onLoadMore = { }, ) } } @@ -445,7 +445,7 @@ private fun BookmarkContentListSuccessPreview() { onRetryClick = {}, onContentClick = {}, onBookmarkClick = {}, - loadMoreContents = {}, + onLoadMore = {}, ) } } From 241b5ec76d7b2ecb034387ed964598cdaf01f097 Mon Sep 17 00:00:00 2001 From: yrsel Date: Sat, 28 Feb 2026 19:00:34 +0900 Subject: [PATCH 20/26] =?UTF-8?q?refactor:=20=EB=B6=81=EB=A7=88=ED=81=AC?= =?UTF-8?q?=20=EB=AA=A9=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20threshold=20=EA=B0=92=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt index 1c2f4098e..aaa536fd1 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt @@ -202,7 +202,7 @@ private fun BookmarkContentList( ) { val pagingState: PagingState = uiState.bookmarkContents val listState = rememberLazyListState() - val threshold = 1 + val threshold = 3 val shouldLoadMore by remember { derivedStateOf { if (!pagingState.hasNext || pagingState.isAppending || pagingState.errorUiState != ErrorUiState.None || From b1886c79f8c11baa5d88a3a6ab2e97cb6be66083 Mon Sep 17 00:00:00 2001 From: yrsel Date: Sat, 28 Feb 2026 19:04:52 +0900 Subject: [PATCH 21/26] =?UTF-8?q?refactor:=20=EB=B6=81=EB=A7=88=ED=81=AC?= =?UTF-8?q?=20=EC=BD=98=ED=85=90=EC=B8=A0=20=EC=88=98=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20API=20=EC=97=90=EB=9F=AC=EC=97=90=20=EB=8C=80=ED=95=9C=20Pre?= =?UTF-8?q?view=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bookmark/BookmarkContentListScreen.kt | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt index aaa536fd1..a096f1061 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt @@ -396,6 +396,61 @@ private fun BookmarkContentListErrorPreview() { } } +@Preview(showBackground = true, name = "북마크 콘텐츠 수 조회 API만 에러") +@Composable +private fun BookmarkContentListLoadCountErrorPreview() { + val contents = + persistentListOf( + BookmarkContent( + content = + Content( + 1L, + Creator(1L, "채널명", ""), + VideoData("콘텐츠 제목이 길면 ...으로 표시되는 것을 확인 ㅇㅇㅇ", "thumbnail", "2026-01-12"), + City("대구"), + true, + ), + tripDuration = TripDuration(1, 2), + tripPlaceCount = 2, + ), + BookmarkContent( + content = + Content( + 2L, + Creator(1L, "채널명이 길어지는 경우 채널명이 길어지는 경우 채널명이 길어지는 경우 채널명이 길어지는 경우 ", ""), + VideoData("콘텐츠 제목", "thumbnail", "2025-01-12"), + City("대구"), + true, + ), + tripDuration = TripDuration(0, 1), + tripPlaceCount = 2, + ), + ) + TuripTheme { + Column { + BookmarkContentListContent( + uiState = + BookmarkContentListUiState( + isLoading = false, + bookmarkContents = + PagingState( + items = contents, + hasNext = false, + isAppending = false, + errorUiState = ErrorUiState.None, + ), + totalBookmarkCount = null, + errorUiState = ErrorUiState.None, + ), + onRetryClick = {}, + onContentClick = {}, + onBookmarkClick = {}, + onLoadMore = {}, + ) + } + } +} + @Preview(showBackground = true, name = "정상") @Composable private fun BookmarkContentListSuccessPreview() { From 170643d815ae7be63fcbebf51d4adffa823da6c5 Mon Sep 17 00:00:00 2001 From: yrsel Date: Sat, 28 Feb 2026 22:45:21 +0900 Subject: [PATCH 22/26] =?UTF-8?q?refactor:=20=EB=B6=81=EB=A7=88=ED=81=AC?= =?UTF-8?q?=20=EB=AA=A9=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EB=8D=94=EB=B3=B4=EA=B8=B0=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EC=97=90=20=EB=8C=80=ED=95=9C=20preview=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bookmark/BookmarkContentListScreen.kt | 63 ++++++++++++++++++- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt index a096f1061..0bb22dd45 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListScreen.kt @@ -306,12 +306,15 @@ private fun BookmarkContentList( } @Composable -private fun LoadMoreError(onRetryClick: () -> Unit) { +private fun LoadMoreError( + onRetryClick: () -> Unit, + modifier: Modifier = Modifier, +) { Row( modifier = - Modifier + modifier .fillMaxWidth() - .padding(horizontal = TuripTheme.spacing.large), + .padding(horizontal = TuripTheme.spacing.extraSmall), verticalAlignment = Alignment.CenterVertically, ) { Text( @@ -451,6 +454,60 @@ private fun BookmarkContentListLoadCountErrorPreview() { } } +@Preview(showBackground = true, name = "더보기 에러") +@Composable +private fun BookmarkContentLoadMoreErrorPreview() { + val contents = + persistentListOf( + BookmarkContent( + content = + Content( + 1L, + Creator(1L, "채널명", ""), + VideoData("콘텐츠 제목", "thumbnail", "2026-01-12"), + City("대구"), + true, + ), + tripDuration = TripDuration(1, 2), + tripPlaceCount = 2, + ), + BookmarkContent( + content = + Content( + 2L, + Creator(2L, "다른 채널", ""), + VideoData("두 번째 콘텐츠", "thumbnail", "2025-01-12"), + City("서울"), + true, + ), + tripDuration = TripDuration(0, 1), + tripPlaceCount = 1, + ), + ) + + TuripTheme { + BookmarkContentListContent( + uiState = + BookmarkContentListUiState( + isLoading = false, + bookmarkContents = + PagingState( + items = contents, + hasNext = true, + isAppending = false, + errorUiState = ErrorUiState.Network, + ), + totalBookmarkCount = 2, + errorUiState = ErrorUiState.None, + ), + onRetryClick = {}, + onContentClick = {}, + onBookmarkClick = {}, + onLoadMore = {}, + ) + } +} + @Preview(showBackground = true, name = "정상") @Composable private fun BookmarkContentListSuccessPreview() { From b1af8844fe2e4f68a2d1dc153759a18299be229e Mon Sep 17 00:00:00 2001 From: yrsel Date: Sat, 28 Feb 2026 23:02:26 +0900 Subject: [PATCH 23/26] =?UTF-8?q?refactor:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20preview=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../turip/ui/compose/mypage/MyPageScreen.kt | 86 ++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageScreen.kt b/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageScreen.kt index 5de78a73f..26e5203d8 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageScreen.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageScreen.kt @@ -19,9 +19,17 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.on.turip.R +import com.on.turip.domain.bookmark.BookmarkContent +import com.on.turip.domain.content.Content +import com.on.turip.domain.content.video.VideoData +import com.on.turip.domain.creator.Creator +import com.on.turip.domain.region.City +import com.on.turip.domain.trip.TripDuration import com.on.turip.ui.common.error.toUiModel import com.on.turip.ui.common.extensions.dismissAndExecute import com.on.turip.ui.common.extensions.showSnackbarWithAction @@ -33,6 +41,9 @@ import com.on.turip.ui.compose.mypage.component.MyPageBookmarkContentSection import com.on.turip.ui.compose.mypage.component.MyPageSettingsSection import com.on.turip.ui.compose.mypage.component.ProfileSection import com.on.turip.ui.compose.mypage.model.InquiryMail +import com.on.turip.ui.compose.mypage.model.ProfileModel +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf @Composable fun MyPageScreen( @@ -223,12 +234,83 @@ private fun MyPageScreenContent( } } +private class MyPageUiStatePreviewProvider : PreviewParameterProvider { + override val values: Sequence = + sequenceOf( + // 둘 다 성공 + MyPageUiState.Idle.copy( + profileState = + MyPageSectionState.Success( + ProfileModel( + 1L, + "닉네임은 최대 2줄 까지 가능하도록 보여지고 있어요 닉네임은 최대 2줄 까지 가능하도록 보여지고 있어요", + null, + ), + ), + bookmarkContentState = MyPageSectionState.Success(previewBookmarkContents()), + ), + // 프로필 & 북마크 에러 + MyPageUiState.Idle.copy( + profileState = MyPageSectionState.Error, + bookmarkContentState = MyPageSectionState.Error, + ), + // 북마크 로딩 + MyPageUiState.Idle.copy( + bookmarkContentState = MyPageSectionState.Loading, + ), + // 로그아웃 다이얼로그 + MyPageUiState.Idle.copy(dialogState = MyPageDialogState.LogoutRequired), + // 회원 탈퇴 다이얼로그 + MyPageUiState.Idle.copy(dialogState = MyPageDialogState.ConfirmWithdraw), + ) +} + +private fun previewBookmarkContents(): ImmutableList = + persistentListOf( + BookmarkContent( + content = + Content( + id = 1L, + creator = Creator(1L, "채널명", ""), + videoData = + VideoData( + title = "콘텐츠 제목이 길면 말줄임 처리되는지 확인합니다", + url = "thumbnail", + uploadedDate = "2026-02-23", + ), + city = City("대구"), + isBookmarked = true, + ), + tripDuration = TripDuration(1, 2), + tripPlaceCount = 3, + ), + BookmarkContent( + content = + Content( + id = 2L, + creator = Creator(2L, "긴 채널명 긴 채널명 긴 채널명", ""), + videoData = + VideoData( + title = "짧은 제목", + url = "thumbnail", + uploadedDate = "2026-02-10", + ), + city = City("제주"), + isBookmarked = true, + ), + tripDuration = TripDuration(0, 1), + tripPlaceCount = 5, + ), + ) + @Preview(showBackground = true) @Composable -private fun MyPageScreenPreview() { +private fun MyPageScreenPreview( + @PreviewParameter(MyPageUiStatePreviewProvider::class) uiState: MyPageUiState, +) { TuripTheme { MyPageScreenContent( - uiState = MyPageUiState.Idle, + uiState = uiState, snackbarHostState = remember { SnackbarHostState() }, onNavigateToAllBookmarkContents = {}, onNavigateToContent = {}, From 6433fa9325f35bf2c2958bdd0376395288621c82 Mon Sep 17 00:00:00 2001 From: yrsel Date: Sat, 28 Feb 2026 23:03:00 +0900 Subject: [PATCH 24/26] refactor: ktlintformat --- .../turip/ui/compose/bookmark/BookmarkContentListViewModel.kt | 2 +- .../main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListViewModel.kt index dc2509f7e..fb3cc11e3 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListViewModel.kt @@ -14,7 +14,6 @@ import com.on.turip.ui.common.error.toUiError import com.on.turip.ui.common.paging.PagingLoadMode import com.on.turip.ui.common.paging.PagingState import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -27,6 +26,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import timber.log.Timber +import javax.inject.Inject @HiltViewModel class BookmarkContentListViewModel @Inject constructor( diff --git a/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt index dd837c277..d35a52198 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt @@ -20,7 +20,6 @@ import com.on.turip.ui.compose.mypage.model.InquiryMail import com.on.turip.ui.compose.mypage.util.AppEnvironmentInfoProvider import com.on.turip.ui.compose.mypage.util.toUiModel import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -33,6 +32,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import timber.log.Timber +import javax.inject.Inject @HiltViewModel class MyPageViewModel @Inject constructor( From 4bb19d5a24d7aa2866d9161d1c520862b93785c6 Mon Sep 17 00:00:00 2001 From: yrsel Date: Sat, 28 Feb 2026 23:24:48 +0900 Subject: [PATCH 25/26] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=EB=9E=98?= =?UTF-8?q?=EB=B9=97=20=EB=A6=AC=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 - 북마크 콘텐츠 타이틀 영역 사용안하는 Modifier 제거, Preview 수정 - 북마크 목록 앱바 뒤로가기버튼 영역 크기 조절 --- .../bookmark/BookmarkContentTitleRow.kt | 16 ++++++++-------- .../component/BookmarkContentListAppBar.kt | 18 ++++++++++++------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/android/app/src/main/java/com/on/turip/ui/common/component/bookmark/BookmarkContentTitleRow.kt b/android/app/src/main/java/com/on/turip/ui/common/component/bookmark/BookmarkContentTitleRow.kt index d2bb0f04e..c46803bb2 100644 --- a/android/app/src/main/java/com/on/turip/ui/common/component/bookmark/BookmarkContentTitleRow.kt +++ b/android/app/src/main/java/com/on/turip/ui/common/component/bookmark/BookmarkContentTitleRow.kt @@ -1,5 +1,6 @@ package com.on.turip.ui.common.component.bookmark +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -10,7 +11,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import com.on.turip.ui.compose.designsystem.theme.TuripTheme -import com.on.turip.ui.compose.home.component.RegionChip @Composable fun BookmarkContentTitleRow( @@ -27,10 +27,7 @@ fun BookmarkContentTitleRow( style = TuripTheme.typography.title2, maxLines = 1, overflow = TextOverflow.Ellipsis, - modifier = - Modifier - .weight(1f) - .padding(), + modifier = Modifier.weight(1f), ) trailing?.invoke() } @@ -43,9 +40,12 @@ private fun BookmarkContentTitleRowWithTrailingPreview() { BookmarkContentTitleRow( title = "콘텐츠 제목이 길어질 경우 말줄임 처리 확인용 텍스트입니다", trailing = { - RegionChip( - regionName = "제주", - modifier = Modifier.padding(start = TuripTheme.spacing.small), + Text( + text = "테스트", + modifier = + Modifier + .background(TuripTheme.colors.primary) + .padding(TuripTheme.spacing.medium), ) }, modifier = diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentListAppBar.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentListAppBar.kt index cb9fbeee8..ccde624b6 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentListAppBar.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/component/BookmarkContentListAppBar.kt @@ -1,15 +1,17 @@ package com.on.turip.ui.compose.bookmark.component -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable 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 com.on.turip.R import com.on.turip.ui.compose.designsystem.component.TuripAppBar import com.on.turip.ui.compose.designsystem.theme.TuripTheme @@ -21,11 +23,15 @@ fun BookmarkContentListAppBar( ) { TuripAppBar( start = { - Icon( - imageVector = Icons.AutoMirrored.Default.ArrowBack, - contentDescription = stringResource(R.string.all_back_description), - modifier = Modifier.clickable(onClick = onBackClick), - ) + IconButton( + onClick = onBackClick, + modifier = Modifier.size(36.dp), + ) { + Icon( + imageVector = Icons.AutoMirrored.Default.ArrowBack, + contentDescription = stringResource(R.string.all_back_description), + ) + } }, center = { Text( From 6fb2e7b12954091ca24b15490a6838033940eb7a Mon Sep 17 00:00:00 2001 From: yrsel Date: Sat, 28 Feb 2026 23:43:08 +0900 Subject: [PATCH 26/26] =?UTF-8?q?feat:=20=EB=B6=81=EB=A7=88=ED=81=AC=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20Cursor=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=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 --- .../datasource/BookmarkRemoteDataSource.kt | 6 ++--- .../DefaultBookmarkRemoteDataSource.kt | 8 +++--- .../repository/DefaultBookmarkRepository.kt | 7 +++-- .../bookmark/repository/BookmarkRepository.kt | 6 ++--- .../on/turip/domain/common/paging/Cursor.kt | 6 +++++ .../bookmark/BookmarkContentListViewModel.kt | 26 +++++++------------ .../ui/compose/mypage/MyPageViewModel.kt | 4 ++- 7 files changed, 28 insertions(+), 35 deletions(-) create mode 100644 android/app/src/main/java/com/on/turip/domain/common/paging/Cursor.kt diff --git a/android/app/src/main/java/com/on/turip/data/bookmark/datasource/BookmarkRemoteDataSource.kt b/android/app/src/main/java/com/on/turip/data/bookmark/datasource/BookmarkRemoteDataSource.kt index f6da52cc7..a62f7af36 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmark/datasource/BookmarkRemoteDataSource.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmark/datasource/BookmarkRemoteDataSource.kt @@ -4,16 +4,14 @@ import com.on.turip.core.result.TuripResult import com.on.turip.data.bookmark.dto.BookmarkAddRequest import com.on.turip.data.bookmark.dto.BookmarkContentsResponse import com.on.turip.data.bookmark.dto.BookmarkCountResponse +import com.on.turip.domain.common.paging.Cursor interface BookmarkRemoteDataSource { suspend fun postBookmark(bookmarkAddRequest: BookmarkAddRequest): TuripResult suspend fun deleteBookmark(contentId: Long): TuripResult - suspend fun getBookmarks( - size: Int, - lastId: Long, - ): TuripResult + suspend fun getBookmarks(cursor: Cursor): TuripResult suspend fun getBookmarkCount(): TuripResult } diff --git a/android/app/src/main/java/com/on/turip/data/bookmark/datasource/DefaultBookmarkRemoteDataSource.kt b/android/app/src/main/java/com/on/turip/data/bookmark/datasource/DefaultBookmarkRemoteDataSource.kt index ebc05706e..c8b8ef50e 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmark/datasource/DefaultBookmarkRemoteDataSource.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmark/datasource/DefaultBookmarkRemoteDataSource.kt @@ -6,6 +6,7 @@ import com.on.turip.data.bookmark.dto.BookmarkContentsResponse import com.on.turip.data.bookmark.dto.BookmarkCountResponse import com.on.turip.data.bookmark.service.BookmarkService import com.on.turip.data.result.safeApiCall +import com.on.turip.domain.common.paging.Cursor import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import javax.inject.Inject @@ -25,12 +26,9 @@ class DefaultBookmarkRemoteDataSource @Inject constructor( safeApiCall { bookmarkService.deleteBookmark(contentId) } } - override suspend fun getBookmarks( - size: Int, - lastId: Long, - ): TuripResult = + override suspend fun getBookmarks(cursor: Cursor): TuripResult = withContext(coroutineContext) { - safeApiCall { bookmarkService.getBookmarks(size, lastId) } + safeApiCall { bookmarkService.getBookmarks(cursor.size, cursor.lastId ?: 0L) } } override suspend fun getBookmarkCount(): TuripResult = diff --git a/android/app/src/main/java/com/on/turip/data/bookmark/repository/DefaultBookmarkRepository.kt b/android/app/src/main/java/com/on/turip/data/bookmark/repository/DefaultBookmarkRepository.kt index 63e7686ee..e3b9b247f 100644 --- a/android/app/src/main/java/com/on/turip/data/bookmark/repository/DefaultBookmarkRepository.kt +++ b/android/app/src/main/java/com/on/turip/data/bookmark/repository/DefaultBookmarkRepository.kt @@ -7,6 +7,7 @@ import com.on.turip.data.bookmark.toDomain import com.on.turip.data.bookmark.toRequestDto import com.on.turip.domain.bookmark.BookmarkContent import com.on.turip.domain.bookmark.repository.BookmarkRepository +import com.on.turip.domain.common.paging.Cursor import com.on.turip.domain.common.paging.Page import javax.inject.Inject @@ -18,10 +19,8 @@ class DefaultBookmarkRepository @Inject constructor( override suspend fun deleteBookmark(contentId: Long): TuripResult = bookmarkRemoteDataSource.deleteBookmark(contentId) - override suspend fun loadBookmarks( - size: Int, - lastId: Long, - ): TuripResult> = bookmarkRemoteDataSource.getBookmarks(size, lastId).mapCatching { it.toDomain() } + override suspend fun loadBookmarks(cursor: Cursor): TuripResult> = + bookmarkRemoteDataSource.getBookmarks(cursor).mapCatching { it.toDomain() } override suspend fun loadBookmarkCount(): TuripResult = bookmarkRemoteDataSource.getBookmarkCount().mapCatching { it.count } } diff --git a/android/app/src/main/java/com/on/turip/domain/bookmark/repository/BookmarkRepository.kt b/android/app/src/main/java/com/on/turip/domain/bookmark/repository/BookmarkRepository.kt index 96bfcb8a7..9d56f7bdf 100644 --- a/android/app/src/main/java/com/on/turip/domain/bookmark/repository/BookmarkRepository.kt +++ b/android/app/src/main/java/com/on/turip/domain/bookmark/repository/BookmarkRepository.kt @@ -2,6 +2,7 @@ package com.on.turip.domain.bookmark.repository import com.on.turip.core.result.TuripResult import com.on.turip.domain.bookmark.BookmarkContent +import com.on.turip.domain.common.paging.Cursor import com.on.turip.domain.common.paging.Page interface BookmarkRepository { @@ -9,10 +10,7 @@ interface BookmarkRepository { suspend fun deleteBookmark(contentId: Long): TuripResult - suspend fun loadBookmarks( - size: Int, - lastId: Long, - ): TuripResult> + suspend fun loadBookmarks(cursor: Cursor): TuripResult> suspend fun loadBookmarkCount(): TuripResult } diff --git a/android/app/src/main/java/com/on/turip/domain/common/paging/Cursor.kt b/android/app/src/main/java/com/on/turip/domain/common/paging/Cursor.kt new file mode 100644 index 000000000..8934499c2 --- /dev/null +++ b/android/app/src/main/java/com/on/turip/domain/common/paging/Cursor.kt @@ -0,0 +1,6 @@ +package com.on.turip.domain.common.paging + +data class Cursor( + val size: Int, + val lastId: Long?, +) diff --git a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListViewModel.kt index fb3cc11e3..0df373135 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/bookmark/BookmarkContentListViewModel.kt @@ -7,6 +7,7 @@ import com.on.turip.core.result.onFailure import com.on.turip.core.result.onSuccess import com.on.turip.domain.bookmark.BookmarkContent import com.on.turip.domain.bookmark.repository.BookmarkRepository +import com.on.turip.domain.common.paging.Cursor import com.on.turip.domain.common.paging.Page import com.on.turip.ui.common.error.ErrorUiState import com.on.turip.ui.common.error.UiError @@ -58,12 +59,17 @@ class BookmarkContentListViewModel @Inject constructor( // 새로고침할 때만 전체 콘텐츠 수 API 호출 if (loadMode == PagingLoadMode.REFRESH) launch { loadBookmarkCount() } - val lastItemId: Long = getLastItemId(loadMode) ?: return@launch + val lastItemId: Long? = + uiState.value.bookmarkContents.items + .lastOrNull() + ?.content + ?.id + val cursor = Cursor(size = PAGE_SIZE, lastId = lastItemId) bookmarkRepository - .loadBookmarks(PAGE_SIZE, lastItemId) + .loadBookmarks(cursor) .onSuccess { result: Page -> - Timber.d("북마크 화면 조회 성공 mode =$loadMode") + Timber.d("북마크 화면 조회 성공 mode = $loadMode, cursor = $cursor") applyBookmarkContents(loadMode, result) }.onFailure { errorType: ErrorType -> Timber.e("북마크 화면 에러 loadMode = $loadMode") @@ -125,20 +131,6 @@ class BookmarkContentListViewModel @Inject constructor( } } - private fun getLastItemId(loadMode: PagingLoadMode) = - when (loadMode) { - PagingLoadMode.REFRESH -> { - 0L - } - - PagingLoadMode.APPEND -> { - uiState.value.bookmarkContents.items - .lastOrNull() - ?.content - ?.id - } - } - private fun applyBookmarkContents( loadMode: PagingLoadMode, result: Page, diff --git a/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt b/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt index d35a52198..c686c5826 100644 --- a/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt +++ b/android/app/src/main/java/com/on/turip/ui/compose/mypage/MyPageViewModel.kt @@ -9,6 +9,7 @@ import com.on.turip.domain.account.Account import com.on.turip.domain.account.AccountRepository import com.on.turip.domain.bookmark.BookmarkContent import com.on.turip.domain.bookmark.repository.BookmarkRepository +import com.on.turip.domain.common.paging.Cursor import com.on.turip.domain.common.paging.Page import com.on.turip.domain.login.MemberRepository import com.on.turip.domain.setting.PrivacyPolicy @@ -73,8 +74,9 @@ class MyPageViewModel @Inject constructor( viewModelScope.launch { _uiState.update { it.copy(bookmarkContentState = MyPageSectionState.Loading) } + val cursor = Cursor(size = 10, lastId = null) bookmarkRepository - .loadBookmarks(10, 0L) + .loadBookmarks(cursor) .onSuccess { result: Page -> Timber.d("마이페이지 북마크 목록 조회 성공") _uiState.update {