diff --git a/Prezel/core/data/src/main/java/com/team/prezel/core/data/mapper/PresentationMapper.kt b/Prezel/core/data/src/main/java/com/team/prezel/core/data/mapper/PresentationMapper.kt index af647911..5c8726ca 100644 --- a/Prezel/core/data/src/main/java/com/team/prezel/core/data/mapper/PresentationMapper.kt +++ b/Prezel/core/data/src/main/java/com/team/prezel/core/data/mapper/PresentationMapper.kt @@ -158,6 +158,10 @@ private fun GetMainDataResponse.GrowthGraph.toDomain(): PresentationGrowthPoint internal fun GetCurationResponse.toDomain(): Curation = Curation( guideMessage = guideMessage, + category = Category.from(value = type), + purpose = Purpose.from(value = purpose), + style = Style.from(value = style), + audience = Audience.from(value = audience), materialType = materialType, title = title, sourceChannel = sourceChannel, diff --git a/Prezel/core/model/src/main/java/com/team/prezel/core/model/presentation/Curation.kt b/Prezel/core/model/src/main/java/com/team/prezel/core/model/presentation/Curation.kt index e1e3ac1e..0abf44ab 100644 --- a/Prezel/core/model/src/main/java/com/team/prezel/core/model/presentation/Curation.kt +++ b/Prezel/core/model/src/main/java/com/team/prezel/core/model/presentation/Curation.kt @@ -2,6 +2,10 @@ package com.team.prezel.core.model.presentation data class Curation( val guideMessage: String, + val category: Category, + val purpose: Purpose, + val style: Style, + val audience: Audience, val materialType: String, val title: String, val sourceChannel: String, diff --git a/Prezel/core/network/src/main/java/com/team/prezel/core/network/model/presentation/GetCurationResponse.kt b/Prezel/core/network/src/main/java/com/team/prezel/core/network/model/presentation/GetCurationResponse.kt index 23aa3371..e801847e 100644 --- a/Prezel/core/network/src/main/java/com/team/prezel/core/network/model/presentation/GetCurationResponse.kt +++ b/Prezel/core/network/src/main/java/com/team/prezel/core/network/model/presentation/GetCurationResponse.kt @@ -7,14 +7,22 @@ import kotlinx.serialization.Serializable data class GetCurationResponse( @SerialName("guideMessage") val guideMessage: String, - @SerialName("imageUrl") - val imageUrl: String, - @SerialName("linkUrl") - val linkUrl: String, + @SerialName("type") + val type: String, + @SerialName("purpose") + val purpose: String, + @SerialName("style") + val style: String, + @SerialName("audience") + val audience: String, @SerialName("materialType") val materialType: String, - @SerialName("sourceChannel") - val sourceChannel: String, @SerialName("title") val title: String, + @SerialName("sourceChannel") + val sourceChannel: String, + @SerialName("linkUrl") + val linkUrl: String, + @SerialName("imageUrl") + val imageUrl: String, ) diff --git a/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/HomeScreen.kt b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/HomeScreen.kt index e2c2f5af..5bc5c2f1 100644 --- a/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/HomeScreen.kt +++ b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/HomeScreen.kt @@ -5,6 +5,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalResources import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel @@ -28,6 +29,7 @@ import com.team.prezel.feature.home.impl.main.model.PracticeRecordsUiModel import com.team.prezel.feature.home.impl.main.model.PresentationUiModel import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.launch import kotlinx.datetime.LocalDate @Composable @@ -44,6 +46,7 @@ internal fun HomeScreen( val pagerState = rememberPagerState(0) { uiState.presentationCount() } val snackbarHostState = LocalSnackbarHostState.current val resources = LocalResources.current + val scope = rememberCoroutineScope() LaunchedEffect(Unit) { viewModel.onIntent(HomeUiIntent.FetchData) @@ -80,6 +83,13 @@ internal fun HomeScreen( onClickCardGraphItemIndex = { presentationId, index -> viewModel.onIntent(HomeUiIntent.ClickCardGraphItem(presentationId = presentationId, index = index)) }, + onCurationLinkOpenFailed = { + scope.launch { + snackbarHostState.showPrezelSnackbar( + message = resources.getString(R.string.feature_home_impl_open_curation_link_failed), + ) + } + }, modifier = modifier, ) } @@ -156,6 +166,7 @@ private fun HomeScreenPreview(uiState: HomeUiState) { onClickVoiceRecordingAnalysis = {}, onClickFileUploadAnalysis = {}, onClickCardGraphItemIndex = { _, _ -> }, + onCurationLinkOpenFailed = {}, ) } } diff --git a/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/HomeScreenContent.kt b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/HomeScreenContent.kt index c8773de4..bc47a791 100644 --- a/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/HomeScreenContent.kt +++ b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/HomeScreenContent.kt @@ -42,6 +42,7 @@ internal fun HomeScreenContent( onClickVoiceRecordingAnalysis: () -> Unit, onClickFileUploadAnalysis: () -> Unit, onClickCardGraphItemIndex: (presentationId: Long, index: Int) -> Unit, + onCurationLinkOpenFailed: () -> Unit, modifier: Modifier = Modifier, ) { val scope = rememberCoroutineScope() @@ -85,6 +86,7 @@ internal fun HomeScreenContent( onClickAnalyzePresentation = onClickAnalyzePresentation, onClickWriteFeedback = onClickWriteFeedback, onClickCardGraphItemIndex = onClickCardGraphItemIndex, + onCurationLinkOpenFailed = onCurationLinkOpenFailed, ) } } @@ -102,6 +104,7 @@ private fun HomeContent( onClickAnalyzePresentation: (PresentationUiModel) -> Unit, onClickWriteFeedback: (PresentationUiModel) -> Unit, onClickCardGraphItemIndex: (presentationId: Long, index: Int) -> Unit, + onCurationLinkOpenFailed: () -> Unit, ) { when (uiState) { HomeUiState.Loading -> Unit @@ -123,6 +126,7 @@ private fun HomeContent( onClickAnalyzePresentation = onClickAnalyzePresentation, onClickWriteFeedback = onClickWriteFeedback, onClickCardGraphItemIndex = { index -> onClickCardGraphItemIndex(uiState.presentation.id, index) }, + onCurationLinkOpenFailed = onCurationLinkOpenFailed, ) } @@ -136,6 +140,7 @@ private fun HomeContent( onClickAnalyzePresentation = onClickAnalyzePresentation, onClickWriteFeedback = onClickWriteFeedback, onClickCardGraphItemIndex = onClickCardGraphItemIndex, + onCurationLinkOpenFailed = onCurationLinkOpenFailed, ) } } @@ -170,6 +175,7 @@ private fun HomePresentationContent( onClickAnalyzePresentation: (PresentationUiModel) -> Unit, onClickWriteFeedback: (PresentationUiModel) -> Unit, onClickCardGraphItemIndex: (index: Int) -> Unit, + onCurationLinkOpenFailed: () -> Unit, ) { HomePageLayout( maxHeight = maxHeight, @@ -179,6 +185,7 @@ private fun HomePresentationContent( presentation = presentation, onClickPracticeRecording = onClickPracticeRecording, onClickCardGraphItemIndex = onClickCardGraphItemIndex, + onCurationLinkOpenFailed = onCurationLinkOpenFailed, ) }, heroContent = { @@ -201,6 +208,7 @@ private fun HomeMultipleContent( onClickAnalyzePresentation: (PresentationUiModel) -> Unit, onClickWriteFeedback: (PresentationUiModel) -> Unit, onClickCardGraphItemIndex: (presentationId: Long, index: Int) -> Unit, + onCurationLinkOpenFailed: () -> Unit, ) { HorizontalPager( state = pagerState, @@ -219,6 +227,7 @@ private fun HomeMultipleContent( onClickAnalyzePresentation = onClickAnalyzePresentation, onClickWriteFeedback = onClickWriteFeedback, onClickCardGraphItemIndex = { index -> onClickCardGraphItemIndex(presentation.id, index) }, + onCurationLinkOpenFailed = onCurationLinkOpenFailed, ) } } diff --git a/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/CurationCard.kt b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/CurationCard.kt new file mode 100644 index 00000000..355169b1 --- /dev/null +++ b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/CurationCard.kt @@ -0,0 +1,127 @@ +package com.team.prezel.feature.home.impl.main.component.body + +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.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.material3.Text +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.LocalUriHandler +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.team.prezel.core.designsystem.component.PrezelAsyncImage +import com.team.prezel.core.designsystem.component.chip.chip.ChipSize +import com.team.prezel.core.designsystem.component.chip.chip.ChipType +import com.team.prezel.core.designsystem.component.chip.chip.PrezelChip +import com.team.prezel.core.designsystem.preview.BasicPreview +import com.team.prezel.core.designsystem.theme.PrezelTheme +import com.team.prezel.core.model.presentation.Audience +import com.team.prezel.core.model.presentation.Category +import com.team.prezel.core.model.presentation.Purpose +import com.team.prezel.core.model.presentation.Style +import com.team.prezel.core.ui.util.noRippleClickable +import com.team.prezel.feature.home.impl.main.model.CurationUiModel +import java.net.URI + +@Composable +internal fun CurationCard( + curation: CurationUiModel, + onLinkOpenFailed: () -> Unit, + modifier: Modifier = Modifier, +) { + val uriHandler = LocalUriHandler.current + + Row( + modifier = modifier + .fillMaxWidth() + .noRippleClickable { + if (curation.linkUrl.isSupportedWebUrl()) { + try { + uriHandler.openUri(curation.linkUrl) + } catch (_: IllegalArgumentException) { + onLinkOpenFailed() + } + } else { + onLinkOpenFailed() + } + }, + horizontalArrangement = Arrangement.spacedBy(PrezelTheme.spacing.V16), + ) { + Box( + modifier = Modifier + .size(width = 152.dp, height = 86.dp) + .clip(PrezelTheme.shapes.V4), + ) { + PrezelAsyncImage( + url = curation.imageUrl, + contentDescription = "", + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Crop, + ) + PrezelChip( + text = curation.materialType, + modifier = Modifier.padding(PrezelTheme.spacing.V4), + type = ChipType.FILLED, + size = ChipSize.SMALL, + ) + } + + Column( + modifier = Modifier.height(86.dp), + verticalArrangement = Arrangement.spacedBy(PrezelTheme.spacing.V4), + ) { + Text( + text = curation.title, + color = PrezelTheme.colors.textLarge, + overflow = TextOverflow.Ellipsis, + maxLines = 2, + style = PrezelTheme.typography.caption1Medium, + ) + Text( + text = curation.sourceChannel, + color = PrezelTheme.colors.textSmall, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + style = PrezelTheme.typography.caption2Regular, + ) + } + } +} + +private fun String.isSupportedWebUrl(): Boolean = + runCatching { URI(this) } + .getOrNull() + ?.let { uri -> + !uri.host.isNullOrBlank() && + (uri.scheme.equals("http", ignoreCase = true) || uri.scheme.equals("https", ignoreCase = true)) + } == true + +@BasicPreview +@Composable +private fun CurationCardPreview() { + PrezelTheme { + CurationCard( + curation = CurationUiModel( + guideMessage = "발표 흐름을 키워드별로 정리해보세요", + category = Category.EDUCATION, + purpose = Purpose.INFO, + style = Style.FORMAL, + audience = Audience.GENERAL, + materialType = "아티클", + title = "제목이 두 줄로 넘어가면 자연스럽게 말줄임표로 전환돼요", + sourceChannel = "계정 이름", + linkUrl = "https://example.com", + imageUrl = "", + ), + onLinkOpenFailed = {}, + ) + } +} diff --git a/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/PresentationSheet.kt b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/PresentationSheet.kt index 3d03f78e..6f383f1e 100644 --- a/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/PresentationSheet.kt +++ b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/PresentationSheet.kt @@ -1,20 +1,40 @@ package com.team.prezel.feature.home.impl.main.component.body +import androidx.annotation.StringRes +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +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.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredWidth +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.rememberScrollState +import androidx.compose.material3.Text +import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import com.team.prezel.core.designsystem.component.chip.chip.ChipSize +import com.team.prezel.core.designsystem.component.chip.chip.ChipType +import com.team.prezel.core.designsystem.component.chip.chip.PrezelChip import com.team.prezel.core.designsystem.preview.BasicPreview import com.team.prezel.core.designsystem.theme.PrezelTheme +import com.team.prezel.core.model.presentation.Audience import com.team.prezel.core.model.presentation.Category +import com.team.prezel.core.model.presentation.Purpose +import com.team.prezel.core.model.presentation.Style import com.team.prezel.core.ui.component.PracticeCard import com.team.prezel.core.ui.component.graph.CardGraph import com.team.prezel.feature.home.impl.R +import com.team.prezel.feature.home.impl.main.model.CurationUiModel import com.team.prezel.feature.home.impl.main.model.PracticeRecordsUiModel import com.team.prezel.feature.home.impl.main.model.PresentationUiModel import kotlinx.collections.immutable.persistentListOf @@ -25,24 +45,16 @@ internal fun PresentationSheet( presentation: PresentationUiModel, onClickPracticeRecording: () -> Unit, onClickCardGraphItemIndex: (index: Int) -> Unit, + onCurationLinkOpenFailed: () -> Unit, modifier: Modifier = Modifier, ) { HomeBottomSheetContent( modifier = modifier, contentPadding = PaddingValues(vertical = PrezelTheme.spacing.V32, horizontal = PrezelTheme.spacing.V20), ) { - HomeBottomSheetTitle( - title = if (presentation.practiceCount == 0) { - stringResource(R.string.feature_home_impl_empty_sheet_practice_card_title) - } else { - stringResource(R.string.feature_home_impl_bottom_sheet_content_title, presentation.practiceCount) - }, - ) - PracticeCard( - dDay = presentation.practiceRecords.endDate, - items = presentation.practiceRecords.practices, - showActionButton = !presentation.isPastPresentation, - onClickAction = onClickPracticeRecording, + PresentationPracticeSection( + presentation = presentation, + onClickPracticeRecording = onClickPracticeRecording, ) Spacer(modifier = Modifier.height(PrezelTheme.spacing.V32)) @@ -50,22 +62,116 @@ internal fun PresentationSheet( is PresentationUiModel.Past -> { if (presentation.growthGraphData.graphItems.isEmpty()) return@HomeBottomSheetContent - HomeBottomSheetTitle(title = stringResource(R.string.feature_home_impl_bottom_sheet_past_graph_title)) - CardGraph( - items = presentation.growthGraphData.graphItems, - selectedItemIndex = presentation.growthGraphData.selectedItemIndex, - onSelectItem = { index -> onClickCardGraphItemIndex(index) }, + PastPresentationSection( + presentation = presentation, + onClickCardGraphItemIndex = onClickCardGraphItemIndex, ) } - is PresentationUiModel.Upcoming -> { - HomeBottomSheetTitle(title = stringResource(R.string.feature_home_impl_bottom_sheet_upcoming_keywords_title)) - } + is PresentationUiModel.Upcoming -> UpcomingPresentationSection( + presentation = presentation, + onCurationLinkOpenFailed = onCurationLinkOpenFailed, + ) } Spacer(modifier = Modifier.height(PrezelTheme.spacing.V36)) } } +@Composable +private fun PresentationPracticeSection( + presentation: PresentationUiModel, + onClickPracticeRecording: () -> Unit, +) { + HomeBottomSheetTitle( + title = if (presentation.practiceCount == 0) { + stringResource(R.string.feature_home_impl_empty_sheet_practice_card_title) + } else { + stringResource(R.string.feature_home_impl_bottom_sheet_content_title, presentation.practiceCount) + }, + ) + PracticeCard( + dDay = presentation.practiceRecords.endDate, + items = presentation.practiceRecords.practices, + showActionButton = !presentation.isPastPresentation, + onClickAction = onClickPracticeRecording, + ) +} + +@Composable +private fun PastPresentationSection( + presentation: PresentationUiModel.Past, + onClickCardGraphItemIndex: (index: Int) -> Unit, +) { + HomeBottomSheetTitle(title = stringResource(R.string.feature_home_impl_bottom_sheet_past_graph_title)) + CardGraph( + items = presentation.growthGraphData.graphItems, + selectedItemIndex = presentation.growthGraphData.selectedItemIndex, + onSelectItem = onClickCardGraphItemIndex, + ) +} + +@Composable +private fun UpcomingPresentationSection( + presentation: PresentationUiModel.Upcoming, + onCurationLinkOpenFailed: () -> Unit, +) { + val firstCuration = presentation.curations.firstOrNull() ?: return + + HomeBottomSheetTitle(title = firstCuration.guideMessage) + CurationMetadataRow( + presentationTitle = presentation.title, + curation = firstCuration, + ) + Spacer(modifier = Modifier.height(PrezelTheme.spacing.V16)) + Column(verticalArrangement = Arrangement.spacedBy(PrezelTheme.spacing.V16)) { + presentation.curations.forEach { curation -> + CurationCard( + curation = curation, + onLinkOpenFailed = onCurationLinkOpenFailed, + ) + } + } +} + +@Composable +private fun CurationMetadataRow( + presentationTitle: String, + curation: CurationUiModel, +) { + BoxWithConstraints(modifier = Modifier.fillMaxWidth()) { + Row( + modifier = Modifier + .wrapContentWidth(align = Alignment.Start, unbounded = true) + .requiredWidth(maxWidth + PrezelTheme.spacing.V20) + .horizontalScroll(rememberScrollState()), + horizontalArrangement = Arrangement.spacedBy(PrezelTheme.spacing.V8), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = presentationTitle, + color = PrezelTheme.colors.textMedium, + style = PrezelTheme.typography.caption2Regular, + ) + VerticalDivider( + modifier = Modifier.height(PrezelTheme.spacing.V24), + color = PrezelTheme.colors.borderSmall, + ) + listOf( + curation.category.labelResId(), + curation.purpose.labelResId(), + curation.style.labelResId(), + curation.audience.labelResId(), + ).forEach { labelResId -> + PrezelChip( + text = stringResource(labelResId), + type = ChipType.FILLED, + size = ChipSize.SMALL, + ) + } + } + } +} + @BasicPreview @Composable private fun PresentationContentPreview() { @@ -73,7 +179,7 @@ private fun PresentationContentPreview() { val presentation = PresentationUiModel.Upcoming( id = 1L, category = Category.OFFER, - title = "설득하는 발표", + title = "사용자가 입력한 발표 제목", date = LocalDate(2026, 10, 1), dDay = "D-3", practiceRecords = PracticeRecordsUiModel( @@ -81,7 +187,44 @@ private fun PresentationContentPreview() { startDate = LocalDate(2026, 9, 26), endDate = LocalDate(2026, 10, 1), ), - curations = persistentListOf(), + curations = persistentListOf( + CurationUiModel( + guideMessage = "발표 흐름을 키워드별로 정리해보세요", + category = Category.EDUCATION, + purpose = Purpose.INFO, + style = Style.FORMAL, + audience = Audience.GENERAL, + materialType = "아티클", + title = "제목이 한 줄이라면 이래요", + sourceChannel = "계정 이름", + linkUrl = "https://example.com/article", + imageUrl = "https://picsum.photos/280/160?random=1", + ), + CurationUiModel( + guideMessage = "발표 흐름을 키워드별로 정리해보세요", + category = Category.EDUCATION, + purpose = Purpose.INFO, + style = Style.FORMAL, + audience = Audience.GENERAL, + materialType = "영상", + title = "제목이 두 줄로 넘어가면 자연스럽게 말줄임표로 전환돼요", + sourceChannel = "프레젠테이션 채널", + linkUrl = "https://example.com/video", + imageUrl = "https://picsum.photos/280/160?random=2", + ), + CurationUiModel( + guideMessage = "발표 흐름을 키워드별로 정리해보세요", + category = Category.EDUCATION, + purpose = Purpose.INFO, + style = Style.FORMAL, + audience = Audience.GENERAL, + materialType = "아티클", + title = "청중을 설득하는 발표 구성 방법", + sourceChannel = "발표 연구소", + linkUrl = "https://example.com/presentation", + imageUrl = "https://picsum.photos/280/160?random=3", + ), + ), ) Box(modifier = Modifier.padding(top = 16.dp)) { @@ -89,7 +232,42 @@ private fun PresentationContentPreview() { presentation = presentation, onClickPracticeRecording = {}, onClickCardGraphItemIndex = {}, + onCurationLinkOpenFailed = {}, ) } } } + +@StringRes +private fun Category.labelResId(): Int = + when (this) { + Category.EDUCATION -> R.string.feature_home_impl_category_education + Category.WORK -> R.string.feature_home_impl_category_report + Category.OFFER -> R.string.feature_home_impl_category_persuasion + Category.EVENT -> R.string.feature_home_impl_category_event + } + +@StringRes +private fun Purpose.labelResId(): Int = + when (this) { + Purpose.INFO -> R.string.feature_home_impl_purpose_info + Purpose.UNDERSTANDING -> R.string.feature_home_impl_purpose_understanding + Purpose.EMPATHY -> R.string.feature_home_impl_purpose_empathy + } + +@StringRes +private fun Style.labelResId(): Int = + when (this) { + Style.FORMAL -> R.string.feature_home_impl_style_formal + Style.FRIENDLY -> R.string.feature_home_impl_style_friendly + Style.CALM -> R.string.feature_home_impl_style_calm + Style.CASUAL -> R.string.feature_home_impl_style_casual + } + +@StringRes +private fun Audience.labelResId(): Int = + when (this) { + Audience.GENERAL -> R.string.feature_home_impl_audience_general + Audience.PROFESSIONAL -> R.string.feature_home_impl_audience_professional + Audience.TEAMMATE -> R.string.feature_home_impl_audience_teammate + } diff --git a/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/model/CurationUiModel.kt b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/model/CurationUiModel.kt new file mode 100644 index 00000000..42ae8cb3 --- /dev/null +++ b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/model/CurationUiModel.kt @@ -0,0 +1,38 @@ +package com.team.prezel.feature.home.impl.main.model + +import androidx.compose.runtime.Immutable +import com.team.prezel.core.model.presentation.Audience +import com.team.prezel.core.model.presentation.Category +import com.team.prezel.core.model.presentation.Curation +import com.team.prezel.core.model.presentation.Purpose +import com.team.prezel.core.model.presentation.Style + +@Immutable +internal data class CurationUiModel( + val guideMessage: String, + val category: Category, + val purpose: Purpose, + val style: Style, + val audience: Audience, + val materialType: String, + val title: String, + val sourceChannel: String, + val linkUrl: String, + val imageUrl: String, +) { + companion object { + fun Curation.toUiModel(): CurationUiModel = + CurationUiModel( + guideMessage = guideMessage, + category = category, + purpose = purpose, + style = style, + audience = audience, + materialType = materialType, + title = title, + sourceChannel = sourceChannel, + linkUrl = linkUrl, + imageUrl = imageUrl, + ) + } +} diff --git a/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/model/PresentationUiModel.kt b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/model/PresentationUiModel.kt index 25826b51..a3836ad4 100644 --- a/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/model/PresentationUiModel.kt +++ b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/model/PresentationUiModel.kt @@ -2,9 +2,9 @@ package com.team.prezel.feature.home.impl.main.model import androidx.compose.runtime.Immutable import com.team.prezel.core.model.presentation.Category -import com.team.prezel.core.model.presentation.Curation import com.team.prezel.core.model.presentation.MainDataWithPracticeRecords import com.team.prezel.core.ui.component.PracticeCardItem +import com.team.prezel.feature.home.impl.main.model.CurationUiModel.Companion.toUiModel import com.team.prezel.feature.home.impl.main.model.GrowthGraphData.Companion.toUiModel import com.team.prezel.feature.home.impl.main.model.PracticeRecordsUiModel.Companion.toUiModel import kotlinx.collections.immutable.ImmutableList @@ -41,7 +41,7 @@ internal sealed interface PresentationUiModel { override val date: LocalDate, override val dDay: String, override val practiceRecords: PracticeRecordsUiModel, - val curations: ImmutableList, + val curations: ImmutableList, ) : PresentationUiModel companion object { @@ -64,7 +64,7 @@ internal sealed interface PresentationUiModel { date = presentationDate, dDay = dDay, practiceRecords = practiceRecords.toUiModel(), - curations = curations.toImmutableList(), + curations = curations.map { curation -> curation.toUiModel() }.toImmutableList(), ) } } diff --git a/Prezel/feature/home/impl/src/main/res/values/strings.xml b/Prezel/feature/home/impl/src/main/res/values/strings.xml index 768d87d8..151e7ae6 100644 --- a/Prezel/feature/home/impl/src/main/res/values/strings.xml +++ b/Prezel/feature/home/impl/src/main/res/values/strings.xml @@ -4,7 +4,6 @@ 지금부터 연습해보세요 지금까지 %1$d번 연습했어요 나의 발표 변화를 확인해보세요 - 발표 흐름을 키워드별로 정리해보세요 안녕하세요 %1$s님! 어떤 발표를 앞두고 있나요? %1$d년 %2$02d월 %3$02d일 @@ -21,6 +20,16 @@ 행사·공개 학술·교육 업무·보고 + 정보 전달 + 이해도 향상 + 공감대 형성 + 전문적인 + 친근한 + 격식있는 + 일상적인 + 대중 + 교수/교사/강사 + 팀/동료 연습하기 @@ -30,4 +39,5 @@ 데이터를 불러오지 못했습니다. + 링크를 열 수 없습니다.