diff --git a/core/ui/src/main/kotlin/com/afternote/core/ui/icon/AfternoteSourceIcon.kt b/core/ui/src/main/kotlin/com/afternote/core/ui/icon/AfternoteSourceIcon.kt new file mode 100644 index 0000000000..1066e673cf --- /dev/null +++ b/core/ui/src/main/kotlin/com/afternote/core/ui/icon/AfternoteSourceIcon.kt @@ -0,0 +1,25 @@ +package com.afternote.core.ui.icon + +import androidx.annotation.DrawableRes +import com.afternote.core.ui.R + +/** + * 애프터노트 **종류** 아이콘의 core 정본 (#926). + * + * 수신자 홈이 이 아이콘을 쓰는데, 종전에는 `feature:afternote:presentation` 의 `R` 을 직접 + * 빌려 왔다. 형제 feature 의 리소스를 가로지르는 참조라 두 모듈이 리소스 수준에서 묶인다. + * + * 종류는 도메인 개념이지만 **아이콘 선택은 표현**이라, 화면 사이에 오가는 값은 res id 가 + * 아니라 이 enum 이다. ViewModel 이 res id 를 들면 상태가 Android 리소스에 묶여 + * 단위 테스트에서도 리소스가 필요해진다. + */ +enum class AfternoteSourceIcon( + @param:DrawableRes val drawableResId: Int, +) { + SocialNetwork(R.drawable.core_ui_afternote_social_pattern), + GalleryAndFiles(R.drawable.core_ui_afternote_gallery_pattern), + Memorial(R.drawable.core_ui_afternote_memorial_guideline), + + /** 위 셋으로 갈리지 않는 종류 — 로고로 받는다. */ + Other(R.drawable.core_ui_afternote_logo), +} diff --git a/feature/afternote/presentation/src/main/res/drawable/feature_afternote_ic_gallery_pattern.xml b/core/ui/src/main/res/drawable/core_ui_afternote_gallery_pattern.xml similarity index 100% rename from feature/afternote/presentation/src/main/res/drawable/feature_afternote_ic_gallery_pattern.xml rename to core/ui/src/main/res/drawable/core_ui_afternote_gallery_pattern.xml diff --git a/feature/afternote/presentation/src/main/res/drawable/feature_afternote_img_logo.png b/core/ui/src/main/res/drawable/core_ui_afternote_logo.png similarity index 100% rename from feature/afternote/presentation/src/main/res/drawable/feature_afternote_img_logo.png rename to core/ui/src/main/res/drawable/core_ui_afternote_logo.png diff --git a/feature/afternote/presentation/src/main/res/drawable/feature_afternote_ic_memorial_guideline.xml b/core/ui/src/main/res/drawable/core_ui_afternote_memorial_guideline.xml similarity index 100% rename from feature/afternote/presentation/src/main/res/drawable/feature_afternote_ic_memorial_guideline.xml rename to core/ui/src/main/res/drawable/core_ui_afternote_memorial_guideline.xml diff --git a/feature/afternote/presentation/src/main/res/drawable/feature_afternote_ic_social_pattern.xml b/core/ui/src/main/res/drawable/core_ui_afternote_social_pattern.xml similarity index 100% rename from feature/afternote/presentation/src/main/res/drawable/feature_afternote_ic_social_pattern.xml rename to core/ui/src/main/res/drawable/core_ui_afternote_social_pattern.xml diff --git a/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/body/infinite/content/AfternoteListContent.kt b/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/body/infinite/content/AfternoteListContent.kt index d8da38ca99..9a7b390e62 100644 --- a/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/body/infinite/content/AfternoteListContent.kt +++ b/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/body/infinite/content/AfternoteListContent.kt @@ -21,6 +21,7 @@ import com.afternote.feature.afternote.presentation.author.home.AfternoteCategor import com.afternote.feature.afternote.presentation.shared.body.infinite.content.list.AfternoteList import com.afternote.feature.afternote.presentation.shared.body.infinite.content.list.item.ListItemUiModel import kotlinx.coroutines.flow.flowOf +import com.afternote.core.ui.R as CoreUiR @Composable fun AfternoteListContent( @@ -73,14 +74,14 @@ private fun AfternoteListContentPreview() { id = 1L, serviceName = "추억 노트", date = "2025.12.01", - iconResId = R.drawable.feature_afternote_img_logo, + iconResId = CoreUiR.drawable.core_ui_afternote_logo, type = AfternoteType.MEMORIAL, ), ListItemUiModel( id = 2L, serviceName = "인스타그램", date = "2025.11.26", - iconResId = R.drawable.feature_afternote_img_logo, + iconResId = CoreUiR.drawable.core_ui_afternote_logo, type = AfternoteType.SOCIAL_NETWORK, ), ), diff --git a/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/detail/AfternoteDetailServiceHeader.kt b/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/detail/AfternoteDetailServiceHeader.kt index 637375699e..fc2601c450 100644 --- a/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/detail/AfternoteDetailServiceHeader.kt +++ b/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/detail/AfternoteDetailServiceHeader.kt @@ -23,6 +23,7 @@ import com.afternote.core.ui.theme.AfternoteDesign import com.afternote.core.ui.theme.AfternoteTheme import com.afternote.feature.afternote.presentation.R import com.afternote.feature.afternote.presentation.shared.model.AfternoteServiceDisplay +import com.afternote.core.ui.R as CoreUiR /** * 애프터노트 작성자 상세 공통 상단: 서비스 아이콘·이름·최종 작성일. @@ -78,7 +79,7 @@ private fun AfternoteDetailServiceHeaderPreview() { service = AfternoteServiceDisplay( serviceName = "서비스 이름", - iconResId = R.drawable.feature_afternote_ic_memorial_guideline, + iconResId = CoreUiR.drawable.core_ui_afternote_memorial_guideline, ), finalWriteDate = "2024.05.20", ) diff --git a/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/model/AfternoteService.kt b/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/model/AfternoteService.kt index ab4e44ad88..17a1fd5558 100644 --- a/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/model/AfternoteService.kt +++ b/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/model/AfternoteService.kt @@ -3,6 +3,7 @@ package com.afternote.feature.afternote.presentation.shared.model import androidx.annotation.DrawableRes import androidx.annotation.StringRes import com.afternote.feature.afternote.presentation.R +import com.afternote.core.ui.R as CoreUiR /** * Maps API [typeKey] (when present), UI display keys / service titles, and label + icon resources. @@ -19,19 +20,19 @@ enum class AfternoteService( typeKey = "SOCIAL_NETWORK", displayKey = "SOCIAL_NETWORK", stringResId = R.string.afternote_category_social_network, - iconResId = R.drawable.feature_afternote_ic_social_pattern, + iconResId = CoreUiR.drawable.core_ui_afternote_social_pattern, ), GALLERY_AND_FILES_CATEGORY( typeKey = "GALLERY_AND_FILES", displayKey = "GALLERY_AND_FILES", stringResId = R.string.receiver_afternote_item_gallery, - iconResId = R.drawable.feature_afternote_ic_gallery_pattern, + iconResId = CoreUiR.drawable.core_ui_afternote_gallery_pattern, ), MEMORIAL_CATEGORY( typeKey = "MEMORIAL", displayKey = "MEMORIAL", stringResId = R.string.receiver_afternote_item_memorial_guideline, - iconResId = R.drawable.feature_afternote_ic_memorial_guideline, + iconResId = CoreUiR.drawable.core_ui_afternote_memorial_guideline, ), INSTAGRAM( typeKey = "INSTAGRAM", @@ -43,13 +44,13 @@ enum class AfternoteService( typeKey = "GALLERY", displayKey = "갤러리", stringResId = R.string.receiver_afternote_item_gallery, - iconResId = R.drawable.feature_afternote_ic_gallery_pattern, + iconResId = CoreUiR.drawable.core_ui_afternote_gallery_pattern, ), MEMORIAL_GUIDELINE( typeKey = "GUIDE", displayKey = "추억 노트", stringResId = R.string.receiver_afternote_item_memorial_guideline, - iconResId = R.drawable.feature_afternote_ic_memorial_guideline, + iconResId = CoreUiR.drawable.core_ui_afternote_memorial_guideline, ), NAVER_MAIL( typeKey = "NAVER_MAIL", @@ -175,7 +176,7 @@ enum class AfternoteService( typeKey = null, displayKey = "파일", stringResId = R.string.receiver_afternote_item_gallery, - iconResId = R.drawable.feature_afternote_ic_gallery_pattern, + iconResId = CoreUiR.drawable.core_ui_afternote_gallery_pattern, ), ; diff --git a/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/util/AfternoteDisplayRes.kt b/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/util/AfternoteDisplayRes.kt index feefcc2f74..3618581ffc 100644 --- a/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/util/AfternoteDisplayRes.kt +++ b/feature/afternote/presentation/src/main/kotlin/com/afternote/feature/afternote/presentation/shared/util/AfternoteDisplayRes.kt @@ -4,6 +4,7 @@ import androidx.annotation.StringRes import com.afternote.feature.afternote.domain.AfternoteType import com.afternote.feature.afternote.presentation.R import com.afternote.feature.afternote.presentation.shared.model.AfternoteService +import com.afternote.core.ui.R as CoreUiR /** * 종류 필터 탭 순서. `null` 은 "전체" 탭이다. @@ -32,7 +33,7 @@ fun categoryLabelResFor(type: AfternoteType?): Int = /** Icon drawable res for an [AfternoteType]. */ fun getIconResForType(type: AfternoteType): Int = AfternoteService.fromTypeKeyOrNull(type.name)?.iconResId - ?: R.drawable.feature_afternote_img_logo + ?: CoreUiR.drawable.core_ui_afternote_logo /** * Icon drawable res for a service title shown on a card. diff --git a/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreen.kt b/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreen.kt index 35304b8af2..e8d8feb085 100644 --- a/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreen.kt +++ b/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreen.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.afternote.core.ui.button.AfternoteButton import com.afternote.core.ui.button.AfternoteButtonType +import com.afternote.core.ui.icon.AfternoteSourceIcon import com.afternote.core.ui.popup.Popup import com.afternote.core.ui.popup.PopupType import com.afternote.core.ui.theme.AfternoteDesign @@ -38,12 +39,10 @@ import com.afternote.feature.receiver.presentation.home.component.AfternoteSecti import com.afternote.feature.receiver.presentation.home.component.MindRecordSection import com.afternote.feature.receiver.presentation.home.component.SenderMessageHeroCard import com.afternote.feature.receiver.presentation.home.component.TimeLetterSection -import com.afternote.feature.receiver.presentation.home.model.AfternoteSourceIcon import com.afternote.feature.receiver.presentation.home.model.MindRecordSummary import com.afternote.feature.receiver.presentation.home.model.ReceiverDownloadState import com.afternote.feature.receiver.presentation.home.model.ReceiverHomeUiState import com.afternote.feature.receiver.presentation.home.model.SenderMessage -import com.afternote.feature.afternote.presentation.R as AfternoteFeatureR /** * 수신자 홈 화면 — 한 마디 + 마음의 기록·타임레터·애프터노트 카드 + 모든 기록 내려받기 버튼. @@ -236,10 +235,10 @@ private fun ReceiverHomeScreenPreview() { afternoteTotalCount = 10, afternoteIcons = listOf( - AfternoteSourceIcon(AfternoteFeatureR.drawable.feature_afternote_img_insta_pattern), - AfternoteSourceIcon(AfternoteFeatureR.drawable.feature_afternote_img_googlephoto_pattern), - AfternoteSourceIcon(AfternoteFeatureR.drawable.feature_afternote_img_naver_mail_pattern), - AfternoteSourceIcon(AfternoteFeatureR.drawable.feature_afternote_img_kakaotalk_pattern), + AfternoteSourceIcon.SocialNetwork, + AfternoteSourceIcon.GalleryAndFiles, + AfternoteSourceIcon.Memorial, + AfternoteSourceIcon.Other, ), ), onEvent = {}, diff --git a/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeViewModel.kt b/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeViewModel.kt index 73b46b0195..abc93ddc60 100644 --- a/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeViewModel.kt +++ b/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeViewModel.kt @@ -4,13 +4,13 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.afternote.core.common.reporting.ErrorReporter import com.afternote.core.common.result.runCatchingCancellable -import com.afternote.feature.afternote.presentation.shared.util.getIconResForType +import com.afternote.core.ui.icon.AfternoteSourceIcon +import com.afternote.feature.afternote.domain.AfternoteType import com.afternote.feature.mindrecord.domain.model.ReceiverMindRecords import com.afternote.feature.mindrecord.domain.repository.MindRecordReceiverRepository import com.afternote.feature.receiver.domain.model.AfterNoteListItem import com.afternote.feature.receiver.domain.repository.ReceiverRepository import com.afternote.feature.receiver.presentation.R -import com.afternote.feature.receiver.presentation.home.model.AfternoteSourceIcon import com.afternote.feature.receiver.presentation.home.model.MindRecordSummary import com.afternote.feature.receiver.presentation.home.model.ReceiverDownloadState import com.afternote.feature.receiver.presentation.home.model.ReceiverHomeUiState @@ -183,5 +183,19 @@ private fun List.toAfternoteIcons(): List AfternoteSourceIcon.SocialNetwork + AfternoteType.GALLERY_AND_FILES -> AfternoteSourceIcon.GalleryAndFiles + AfternoteType.MEMORIAL -> AfternoteSourceIcon.Memorial + AfternoteType.BUSINESS, AfternoteType.ESTATE -> AfternoteSourceIcon.Other + } diff --git a/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/component/AfternoteSection.kt b/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/component/AfternoteSection.kt index fb949c1d12..0249a6e230 100644 --- a/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/component/AfternoteSection.kt +++ b/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/component/AfternoteSection.kt @@ -22,11 +22,10 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.afternote.core.ui.icon.AfternoteSourceIcon import com.afternote.core.ui.theme.AfternoteDesign import com.afternote.core.ui.theme.AfternoteTheme import com.afternote.feature.receiver.presentation.R -import com.afternote.feature.receiver.presentation.home.model.AfternoteSourceIcon -import com.afternote.feature.afternote.presentation.R as AfternoteFeatureR private const val MAX_VISIBLE_ICONS = 4 private val ICON_SIZE = 32.dp @@ -127,10 +126,10 @@ private fun AfternoteSectionPreview() { totalCount = 10, icons = listOf( - AfternoteSourceIcon(AfternoteFeatureR.drawable.feature_afternote_img_insta_pattern), - AfternoteSourceIcon(AfternoteFeatureR.drawable.feature_afternote_img_googlephoto_pattern), - AfternoteSourceIcon(AfternoteFeatureR.drawable.feature_afternote_img_naver_mail_pattern), - AfternoteSourceIcon(AfternoteFeatureR.drawable.feature_afternote_img_kakaotalk_pattern), + AfternoteSourceIcon.SocialNetwork, + AfternoteSourceIcon.GalleryAndFiles, + AfternoteSourceIcon.Memorial, + AfternoteSourceIcon.Other, ), onGoClick = {}, modifier = Modifier.padding(20.dp), diff --git a/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/model/ReceiverHomeUiState.kt b/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/model/ReceiverHomeUiState.kt index c42a27cf69..003442b4bf 100644 --- a/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/model/ReceiverHomeUiState.kt +++ b/feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/model/ReceiverHomeUiState.kt @@ -2,6 +2,7 @@ package com.afternote.feature.receiver.presentation.home.model import androidx.annotation.StringRes import androidx.compose.runtime.Immutable +import com.afternote.core.ui.icon.AfternoteSourceIcon /** * 수신자 홈 화면 UI 상태. @@ -46,10 +47,6 @@ data class MindRecordSummary( } @Immutable -data class AfternoteSourceIcon( - val drawableResId: Int, -) - sealed interface ReceiverDownloadState { data object Idle : ReceiverDownloadState diff --git a/feature/receiver/presentation/src/screenshotTest/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreenScreenshotTest.kt b/feature/receiver/presentation/src/screenshotTest/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreenScreenshotTest.kt index f5808e71ce..d31708a735 100644 --- a/feature/receiver/presentation/src/screenshotTest/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreenScreenshotTest.kt +++ b/feature/receiver/presentation/src/screenshotTest/kotlin/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreenScreenshotTest.kt @@ -2,8 +2,8 @@ package com.afternote.feature.receiver.presentation.home import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview +import com.afternote.core.ui.icon.AfternoteSourceIcon import com.afternote.core.ui.theme.AfternoteTheme -import com.afternote.feature.receiver.presentation.home.model.AfternoteSourceIcon import com.afternote.feature.receiver.presentation.home.model.MindRecordSummary import com.afternote.feature.receiver.presentation.home.model.ReceiverHomeUiState import com.afternote.feature.receiver.presentation.home.model.SenderMessage @@ -50,7 +50,7 @@ internal fun receiverHomeScreenSuccessScreenshot() { afternoteTotalCount = 5, afternoteIcons = listOf( - AfternoteSourceIcon(drawableResId = com.afternote.core.ui.R.drawable.core_ui_ic_tabler_search), + AfternoteSourceIcon.SocialNetwork, ), ), onEvent = {}, diff --git a/feature/receiver/presentation/src/screenshotTest/kotlin/com/afternote/feature/receiver/presentation/home/component/AfternoteSectionScreenshotTest.kt b/feature/receiver/presentation/src/screenshotTest/kotlin/com/afternote/feature/receiver/presentation/home/component/AfternoteSectionScreenshotTest.kt index 7c592fdc3d..f8d11804c4 100644 --- a/feature/receiver/presentation/src/screenshotTest/kotlin/com/afternote/feature/receiver/presentation/home/component/AfternoteSectionScreenshotTest.kt +++ b/feature/receiver/presentation/src/screenshotTest/kotlin/com/afternote/feature/receiver/presentation/home/component/AfternoteSectionScreenshotTest.kt @@ -5,8 +5,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.afternote.core.ui.icon.AfternoteSourceIcon import com.afternote.core.ui.theme.AfternoteTheme -import com.afternote.feature.receiver.presentation.home.model.AfternoteSourceIcon import com.android.tools.screenshot.PreviewTest @PreviewTest @@ -18,8 +18,8 @@ internal fun afternoteSectionScreenshot() { totalCount = 5, icons = listOf( - AfternoteSourceIcon(drawableResId = com.afternote.core.ui.R.drawable.core_ui_ic_tabler_search), - AfternoteSourceIcon(drawableResId = com.afternote.core.ui.R.drawable.core_ui_ic_link), + AfternoteSourceIcon.SocialNetwork, + AfternoteSourceIcon.GalleryAndFiles, ), onGoClick = {}, modifier = Modifier.padding(16.dp), diff --git a/feature/receiver/presentation/src/screenshotTestDebug/reference/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreenScreenshotTestKt/receiverHomeScreenSuccessScreenshot_748aa731_0.png b/feature/receiver/presentation/src/screenshotTestDebug/reference/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreenScreenshotTestKt/receiverHomeScreenSuccessScreenshot_748aa731_0.png index 8bda1744f6..980271244c 100644 Binary files a/feature/receiver/presentation/src/screenshotTestDebug/reference/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreenScreenshotTestKt/receiverHomeScreenSuccessScreenshot_748aa731_0.png and b/feature/receiver/presentation/src/screenshotTestDebug/reference/com/afternote/feature/receiver/presentation/home/ReceiverHomeScreenScreenshotTestKt/receiverHomeScreenSuccessScreenshot_748aa731_0.png differ diff --git a/feature/receiver/presentation/src/screenshotTestDebug/reference/com/afternote/feature/receiver/presentation/home/component/AfternoteSectionScreenshotTestKt/afternoteSectionScreenshot_748aa731_0.png b/feature/receiver/presentation/src/screenshotTestDebug/reference/com/afternote/feature/receiver/presentation/home/component/AfternoteSectionScreenshotTestKt/afternoteSectionScreenshot_748aa731_0.png index 17c81fda53..c22c17f648 100644 Binary files a/feature/receiver/presentation/src/screenshotTestDebug/reference/com/afternote/feature/receiver/presentation/home/component/AfternoteSectionScreenshotTestKt/afternoteSectionScreenshot_748aa731_0.png and b/feature/receiver/presentation/src/screenshotTestDebug/reference/com/afternote/feature/receiver/presentation/home/component/AfternoteSectionScreenshotTestKt/afternoteSectionScreenshot_748aa731_0.png differ diff --git a/konsist/src/test/kotlin/com/afternote/konsist/ReceiverHomeResourceKonsistTest.kt b/konsist/src/test/kotlin/com/afternote/konsist/ReceiverHomeResourceKonsistTest.kt new file mode 100644 index 0000000000..b6796cb447 --- /dev/null +++ b/konsist/src/test/kotlin/com/afternote/konsist/ReceiverHomeResourceKonsistTest.kt @@ -0,0 +1,43 @@ +package com.afternote.konsist + +import com.lemonappdev.konsist.api.Konsist +import com.lemonappdev.konsist.api.ext.list.withPackage +import com.lemonappdev.konsist.api.verify.assertFalse +import org.junit.Test + +/** + * 수신자 홈이 형제 feature 의 리소스를 빌려 쓰지 않는지 (#926). + * + * 종전에는 수신자 홈이 `feature:afternote:presentation` 의 `R` 을 직접 import 해 소셜 패턴 + * 아이콘을 그렸다. 리소스는 모듈 경계를 가장 조용히 넘는 의존이다 — 컴파일은 되고, + * 그림도 뜨고, 다른 모듈이 리소스를 지우거나 이름을 바꿀 때에야 드러난다. + * + * 아이콘의 정본은 `core:ui` 이고, 화면 사이에 오가는 값은 res id 가 아니라 + * `AfternoteSourceIcon` 이다. + */ +class ReceiverHomeResourceKonsistTest { + @Test + fun `수신자 홈은 다른 feature 의 R 을 참조하지 않는다`() { + Konsist + .scopeFromProject() + .files + .withPackage("com.afternote.feature.receiver.presentation.home..") + .assertFalse { file -> + file.imports.any { import -> FOREIGN_FEATURE_RESOURCE.matches(import.name) } || + // import 만 보면 FQN 참조가 그대로 빠져나간다 — 이 패키지의 screenshotTest 가 + // 실제로 `com.afternote.core.ui.R.drawable.…` 를 FQN 으로 쓰는 손버릇이라, + // 같은 손버릇으로 형제 feature 의 R 을 쓰면 가드가 침묵한다. 본문도 본다. + FOREIGN_FEATURE_RESOURCE_REFERENCE.containsMatchIn(file.text) + } + } + + private companion object { + /** 수신자 자신이 아닌 feature 의 `R` (또는 그 하위 참조). */ + val FOREIGN_FEATURE_RESOURCE = + Regex("""^com\.afternote\.feature\.(?!receiver\.)[a-z]+\.presentation\.R(\..*)?$""") + + /** 같은 참조를 본문 어디서든 — FQN 으로 쓴 자리를 잡는다. 주석·문자열 오탐은 감수한다. */ + val FOREIGN_FEATURE_RESOURCE_REFERENCE = + Regex("""com\.afternote\.feature\.(?!receiver\.)[a-z]+\.presentation\.R\.""") + } +}