Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,7 @@ import com.kuit.afternote.R
import com.kuit.afternote.app.compositionlocal.DataProviderLocals
import com.kuit.afternote.app.di.ReceiverAuthSessionEntryPoint
import com.kuit.afternote.app.di.TokenManagerEntryPoint
import com.kuit.afternote.core.dummy.receiver.AfternoteListItemSeed
import com.kuit.afternote.core.ui.component.navigation.BottomNavItem
import com.kuit.afternote.core.ui.screen.afternotedetail.GalleryDetailCallbacks
import com.kuit.afternote.core.ui.screen.afternotedetail.GalleryDetailScreen
import com.kuit.afternote.core.ui.screen.afternotedetail.GalleryDetailState
import com.kuit.afternote.core.ui.screen.afternotedetail.MemorialGuidelineDetailCallbacks
import com.kuit.afternote.core.ui.screen.afternotedetail.MemorialGuidelineDetailScreen
import com.kuit.afternote.core.ui.screen.afternotedetail.MemorialGuidelineDetailState
import com.kuit.afternote.core.ui.screen.afternotedetail.SocialNetworkDetailContent
import com.kuit.afternote.core.ui.screen.afternotedetail.SocialNetworkDetailScreen
import com.kuit.afternote.core.ui.screen.afternotedetail.rememberAfternoteDetailState
import com.kuit.afternote.core.uimodel.AfternoteListDisplayItem
import com.kuit.afternote.feature.afternote.domain.model.AfternoteItem
import com.kuit.afternote.feature.afternote.presentation.navgraph.AfternoteEditStateHandling
Expand All @@ -61,6 +51,7 @@ import com.kuit.afternote.feature.home.presentation.screen.HomeScreen
import com.kuit.afternote.feature.home.presentation.screen.HomeScreenEvent
import com.kuit.afternote.feature.onboarding.presentation.navgraph.OnboardingRoute
import com.kuit.afternote.feature.onboarding.presentation.navgraph.onboardingNavGraph
import com.kuit.afternote.feature.receiver.presentation.navgraph.ReceiverAfternoteDetailRoute
import com.kuit.afternote.feature.receiver.presentation.navgraph.ReceiverAfternoteListRoute
import com.kuit.afternote.feature.receiver.presentation.navgraph.ReceiverMainRoute
import com.kuit.afternote.feature.receiver.presentation.navgraph.ReceiverTimeLetterDetailRoute
Expand Down Expand Up @@ -218,72 +209,6 @@ private fun ReceiverAfternoteListRouteContent(navHostController: NavHostControll
)
}

private enum class ReceiverDetailCategory { GALLERY, MEMORIAL_GUIDELINE, SOCIAL }

@Composable
private fun ReceiverAfternoteDetailContent(
navHostController: NavHostController,
itemId: String?
) {
val receiverProvider = DataProviderLocals.LocalReceiverDataProvider.current
val seed =
remember(receiverProvider, itemId) {
receiverProvider
.getAfternoteListSeedsForReceiverList()
.firstOrNull { it.id == itemId }
?: receiverProvider.getAfternoteListSeedsForReceiverList().firstOrNull()
}
val category = receiverDetailCategoryFromSeed(seed)
val serviceName = seed?.serviceNameLiteral ?: ""
val userName = receiverProvider.getDefaultReceiverTitle()
val defaultState = rememberAfternoteDetailState(
defaultBottomNavItem = BottomNavItem.AFTERNOTE
)
when (category) {
ReceiverDetailCategory.GALLERY -> GalleryDetailScreen(
detailState = GalleryDetailState(
serviceName = serviceName.ifEmpty { "갤러리" },
userName = userName,
finalWriteDate = seed?.date ?: ""
),
callbacks = GalleryDetailCallbacks(
onBackClick = { navHostController.popBackStack() },
onEditClick = {}
),
isEditable = false,
uiState = defaultState
)
ReceiverDetailCategory.MEMORIAL_GUIDELINE -> MemorialGuidelineDetailScreen(
detailState = MemorialGuidelineDetailState(
userName = userName,
finalWriteDate = seed?.date ?: ""
),
callbacks = MemorialGuidelineDetailCallbacks(
onBackClick = { navHostController.popBackStack() }
),
isEditable = false,
uiState = defaultState
)
ReceiverDetailCategory.SOCIAL -> SocialNetworkDetailScreen(
content = SocialNetworkDetailContent(
serviceName = serviceName,
userName = userName
),
isEditable = false,
onBackClick = { navHostController.popBackStack() },
state = defaultState
)
}
}

private fun receiverDetailCategoryFromSeed(seed: AfternoteListItemSeed?): ReceiverDetailCategory {
return when (seed?.serviceNameLiteral) {
"갤러리" -> ReceiverDetailCategory.GALLERY
"추모 가이드라인" -> ReceiverDetailCategory.MEMORIAL_GUIDELINE
else -> ReceiverDetailCategory.SOCIAL
}
}

@Composable
private fun HomeScreenContent(
onBottomNavTabSelected: (BottomNavItem) -> Unit,
Expand Down Expand Up @@ -454,7 +379,7 @@ fun NavGraph(navHostController: NavHostController) {
}

composable("receiver_afternote_detail/{itemId}") { backStackEntry ->
ReceiverAfternoteDetailContent(
ReceiverAfternoteDetailRoute(
navHostController = navHostController,
itemId = backStackEntry.arguments?.getString("itemId")
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ private fun MemorialPlaylistAddButton(
* 앨범 행: albumCovers를 사용. albumItemContent를 넘기면 해당 슬롯으로 그리며, null이면 각 앨범을 회색 박스로 표시.
*
* @param onAddSongClick null이면 view 모드(버튼·편집 UI 없음), non-null이면 edit 모드
* @param onPlaylistClick view 모드에서 카드(오른쪽 화살표 영역 포함) 클릭 시 호출. null이면 클릭 비활성화
* @param albumItemContent 앨범 셀 커스텀; null이면 기본 회색 박스 (view/placeholder용)
*/
@Composable
Expand All @@ -137,9 +138,16 @@ fun MemorialPlaylist(
songCount: Int = 0,
albumCovers: List<AlbumCover> = emptyList(),
onAddSongClick: (() -> Unit)? = null,
onPlaylistClick: (() -> Unit)? = null,
albumItemContent: (@Composable (album: AlbumCover, index: Int) -> Unit)? = null
) {
val isEditMode = onAddSongClick != null
val cardModifier = when {
!isEditMode && onPlaylistClick != null -> modifier
.fillMaxWidth()
.clickable(onClick = onPlaylistClick)
else -> modifier.fillMaxWidth()
}
Column(
verticalArrangement = Arrangement.spacedBy(space = 16.dp)
) {
Expand All @@ -154,8 +162,7 @@ fun MemorialPlaylist(
)
)
Column(
modifier = modifier
.fillMaxWidth()
modifier = cardModifier
.background(color = White, shape = RoundedCornerShape(size = 16.dp))
.padding(all = 16.dp),
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,14 @@ interface MindRecordRepository {
suspend fun editMindRecord(recordId: Long, request: PostMindRecordRequest): MindRecordDetailResponse

suspend fun getEmotions(): EmotionResponse

/**
* PATCH mind-records/{recordId}/receivers/{receiverId}
* 해당 마음의 기록을 해당 수신인에게 전달할지 여부를 설정합니다.
*/
suspend fun setMindRecordReceiverEnabled(
recordId: Long,
receiverId: Long,
enabled: Boolean
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.kuit.afternote.feature.dailyrecord.data.dto.MindRecordDetailResponse
import com.kuit.afternote.feature.dailyrecord.data.dto.MindRecordListResponse
import com.kuit.afternote.feature.dailyrecord.data.dto.PostMindRecordRequest
import com.kuit.afternote.feature.dailyrecord.data.dto.PostMindRecordResponse
import com.kuit.afternote.feature.dailyrecord.data.dto.ReceiverEnabledRequest
import com.kuit.afternote.feature.dailyrecord.domain.model.DailyQuestionData
import retrofit2.HttpException
import javax.inject.Inject
Expand Down Expand Up @@ -57,5 +58,20 @@ class MindRecordRepositoryImpl @Inject constructor(
override suspend fun getEmotions(): EmotionResponse {
return apiService.getEmotions().body()!!
}

override suspend fun setMindRecordReceiverEnabled(
recordId: Long,
receiverId: Long,
enabled: Boolean
) {
val response = apiService.setMindRecordReceiverEnabled(
recordId = recordId,
receiverId = receiverId,
request = ReceiverEnabledRequest(enabled = enabled)
)
if (!response.isSuccessful) {
throw HttpException(response)
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.kuit.afternote.feature.dailyrecord.domain.usecase

import com.kuit.afternote.feature.dailyrecord.data.repository.MindRecordRepository
import javax.inject.Inject

/**
* 수신인에게 "나의 모든 마음의 기록" 전달 여부를 일괄 설정합니다.
* PATCH mind-records/{recordId}/receivers/{receiverId} 를
* 현재 사용자의 모든 마음의 기록에 대해 호출합니다.
*/
class SetMindRecordReceiverEnabledForAllUseCase
@Inject
constructor(
private val repository: MindRecordRepository
) {
suspend operator fun invoke(receiverId: Long, enabled: Boolean) {
val recordIds = collectAllMindRecordIds()
recordIds.forEach { recordId ->
repository.setMindRecordReceiverEnabled(
recordId = recordId,
receiverId = receiverId,
enabled = enabled
)
}
}

private suspend fun collectAllMindRecordIds(): List<Long> {
val types = listOf("DAILY_QUESTION", "DIARY", "DEEP_THOUGHT")
val allIds = types.flatMap { type ->
repository.getMindRecords(type, "LIST", null, null)
.data?.records?.map { it.recordId } ?: emptyList()
}
return allIds.distinct()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.kuit.afternote.feature.receiver.presentation.navgraph

import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.navigation.NavHostController
import com.kuit.afternote.app.compositionlocal.DataProviderLocals
import com.kuit.afternote.core.dummy.receiver.AfternoteListItemSeed
import com.kuit.afternote.core.ui.component.navigation.BottomNavItem
import com.kuit.afternote.core.ui.screen.afternotedetail.GalleryDetailCallbacks
import com.kuit.afternote.core.ui.screen.afternotedetail.GalleryDetailScreen
import com.kuit.afternote.core.ui.screen.afternotedetail.GalleryDetailState
import com.kuit.afternote.core.ui.screen.afternotedetail.MemorialGuidelineDetailCallbacks
import com.kuit.afternote.core.ui.screen.afternotedetail.MemorialGuidelineDetailScreen
import com.kuit.afternote.core.ui.screen.afternotedetail.MemorialGuidelineDetailState
import com.kuit.afternote.core.ui.screen.afternotedetail.SocialNetworkDetailContent
import com.kuit.afternote.core.ui.screen.afternotedetail.SocialNetworkDetailScreen
import com.kuit.afternote.core.ui.screen.afternotedetail.rememberAfternoteDetailState

/**
* 수신자 애프터노트 상세 라우트.
* ReceiverDataProvider에서 seed를 조회해 카테고리별 상세 스크린(Gallery/MemorialGuideline/Social)을 표시합니다.
*/
@Composable
fun ReceiverAfternoteDetailRoute(
navHostController: NavHostController,
itemId: String?
) {
val receiverProvider = DataProviderLocals.LocalReceiverDataProvider.current
val seed =
remember(receiverProvider, itemId) {
receiverProvider
.getAfternoteListSeedsForReceiverList()
.firstOrNull { it.id == itemId }
?: receiverProvider.getAfternoteListSeedsForReceiverList().firstOrNull()
}
val category = receiverDetailCategoryFromSeed(seed)
val serviceName = seed?.serviceNameLiteral ?: ""
val userName = receiverProvider.getDefaultReceiverTitle()
val defaultState = rememberAfternoteDetailState(
defaultBottomNavItem = BottomNavItem.AFTERNOTE
)
when (category) {
ReceiverDetailCategory.GALLERY -> GalleryDetailScreen(
detailState = GalleryDetailState(
serviceName = serviceName.ifEmpty { "갤러리" },
userName = userName,
finalWriteDate = seed?.date ?: ""
),
callbacks = GalleryDetailCallbacks(
onBackClick = { navHostController.popBackStack() },
onEditClick = {}
),
isEditable = false,
uiState = defaultState
)
ReceiverDetailCategory.MEMORIAL_GUIDELINE -> MemorialGuidelineDetailScreen(
detailState = MemorialGuidelineDetailState(
userName = userName,
finalWriteDate = seed?.date ?: ""
),
callbacks = MemorialGuidelineDetailCallbacks(
onBackClick = { navHostController.popBackStack() }
),
isEditable = false,
uiState = defaultState
)
ReceiverDetailCategory.SOCIAL -> SocialNetworkDetailScreen(
content = SocialNetworkDetailContent(
serviceName = serviceName,
userName = userName
),
isEditable = false,
onBackClick = { navHostController.popBackStack() },
state = defaultState
)
}
}

private enum class ReceiverDetailCategory { GALLERY, MEMORIAL_GUIDELINE, SOCIAL }

private fun receiverDetailCategoryFromSeed(seed: AfternoteListItemSeed?): ReceiverDetailCategory {
return when (seed?.serviceNameLiteral) {
"갤러리" -> ReceiverDetailCategory.GALLERY
"추모 가이드라인" -> ReceiverDetailCategory.MEMORIAL_GUIDELINE
else -> ReceiverDetailCategory.SOCIAL
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.kuit.afternote.core.ui.component.navigation.BottomNavItem
import com.kuit.afternote.core.ui.component.navigation.BottomNavigationBar
import com.kuit.afternote.feature.receiver.domain.entity.ReceivedTimeLetter
import com.kuit.afternote.feature.receiver.presentation.screen.ReceiverAfterNoteScreen
import com.kuit.afternote.feature.receiver.presentation.navgraph.ReceiverMemorialPlaylistRoute
import com.kuit.afternote.feature.receiver.presentation.screen.afternote.ReceiverAfterNoteMainScreen
import com.kuit.afternote.feature.receiver.presentation.screen.mindrecord.MindRecordDetailScreen
import com.kuit.afternote.feature.receiver.presentation.screen.mindrecord.MindRecordScreen
Expand Down Expand Up @@ -77,6 +78,7 @@ fun ReceiverMainRoute(
var mindRecordSelectedDate by remember { mutableStateOf(LocalDate.now()) }
var showTimeLetterList by remember { mutableStateOf(false) }
var timeLetterListMode by remember { mutableStateOf(TimeLetterListMode.SortByDate) }
var showMemorialPlaylist by remember { mutableStateOf(false) }
val timeLetterViewModel: ReceiverTimeLetterViewModel = hiltViewModel()
val timeLetterUiState by timeLetterViewModel.uiState.collectAsStateWithLifecycle()
val afternoteTriggerViewModel: ReceiverAfternoteTriggerViewModel = hiltViewModel()
Expand All @@ -95,6 +97,9 @@ fun ReceiverMainRoute(
selectedBottomNavItem == BottomNavItem.TIME_LETTER && showTimeLetterList -> {
showTimeLetterList = false
}
selectedBottomNavItem == BottomNavItem.AFTERNOTE && showMemorialPlaylist -> {
showMemorialPlaylist = false
}
selectedBottomNavItem == BottomNavItem.HOME -> {
receiverAuthSessionHolder.clearAuthCode()
navController.popBackStack()
Expand Down Expand Up @@ -230,15 +235,22 @@ fun ReceiverMainRoute(
BottomNavItem.AFTERNOTE -> {
val afternoteSenderName = receiverAuthSessionHolder.getSenderName().orEmpty()
Log.d(TAG_RECEIVER_MAIN, "AFTERNOTE tab getSenderName=${receiverAuthSessionHolder.getSenderName()}, orEmpty='$afternoteSenderName'")
ReceiverAfterNoteMainScreen(
senderName = afternoteSenderName,
albumCovers = albumCovers,
onNavigateToFullList = {
navController.navigate("receiver_afternote_list")
},
onBackClick = { selectedBottomNavItem = BottomNavItem.HOME },
showBottomBar = false
)
if (showMemorialPlaylist) {
ReceiverMemorialPlaylistRoute(
onBackClick = { showMemorialPlaylist = false }
)
} else {
ReceiverAfterNoteMainScreen(
senderName = afternoteSenderName,
albumCovers = albumCovers,
onNavigateToFullList = {
navController.navigate("receiver_afternote_list")
},
onNavigateToPlaylist = { showMemorialPlaylist = true },
onBackClick = { selectedBottomNavItem = BottomNavItem.HOME },
showBottomBar = false
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import com.kuit.afternote.ui.theme.Sansneo
fun ReceiverAfterNoteMainScreen(
senderName: String,
onNavigateToFullList: () -> Unit = {},
onNavigateToPlaylist: () -> Unit = {},
onBackClick: () -> Unit = {},
profileImageResId: Int? = null,
albumCovers: List<AlbumCover>,
Expand Down Expand Up @@ -119,7 +120,8 @@ fun ReceiverAfterNoteMainScreen(
label = "추모 플레이리스트",
songCount = songCount,
albumCovers = albumCovers,
onAddSongClick = null
onAddSongClick = null,
onPlaylistClick = onNavigateToPlaylist
)
},
lastWishContent = {
Expand Down
Loading
Loading