diff --git a/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/tag/TagDestination.kt b/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/tag/TagDestination.kt new file mode 100644 index 00000000..4f7be878 --- /dev/null +++ b/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/tag/TagDestination.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.core.navigation.tag + +import kotlinx.serialization.Serializable + +@Serializable +public data object TagDestination diff --git a/app/core/resources/src/commonMain/composeResources/values-ko/strings.xml b/app/core/resources/src/commonMain/composeResources/values-ko/strings.xml index 67d216a0..ed7c0ec6 100644 --- a/app/core/resources/src/commonMain/composeResources/values-ko/strings.xml +++ b/app/core/resources/src/commonMain/composeResources/values-ko/strings.xml @@ -14,6 +14,7 @@ 제목 설명 날짜 + 태그 %1$d년 %2$d월 %1$d월 %2$d일 diff --git a/app/core/resources/src/commonMain/composeResources/values/strings.xml b/app/core/resources/src/commonMain/composeResources/values/strings.xml index 837fae89..dd88a2eb 100644 --- a/app/core/resources/src/commonMain/composeResources/values/strings.xml +++ b/app/core/resources/src/commonMain/composeResources/values/strings.xml @@ -14,6 +14,7 @@ Title Description Date + Tag %1$d. %2$d. %1$d. %2$d. diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/TagIcon.kt b/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/TagIcon.kt new file mode 100644 index 00000000..c88c9a9c --- /dev/null +++ b/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/TagIcon.kt @@ -0,0 +1,18 @@ +package io.github.taetae98coding.diary.core.resources.icon + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Tag +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +public fun TagIcon( + modifier: Modifier = Modifier, +) { + Icon( + imageVector = Icons.Rounded.Tag, + contentDescription = null, + modifier = modifier, + ) +} diff --git a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailScreen.kt b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailScreen.kt index 9ff3134a..7cc9bd54 100644 --- a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailScreen.kt +++ b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailScreen.kt @@ -73,7 +73,7 @@ internal fun MemoDetailScreen( } } - else -> Unit + is MemoDetailNavigationButton.None -> Unit } }, actions = { @@ -111,7 +111,7 @@ internal fun MemoDetailScreen( } } - else -> Unit + is MemoDetailFloatingButton.None -> Unit } }, ) { diff --git a/app/feature/tag/build.gradle.kts b/app/feature/tag/build.gradle.kts new file mode 100644 index 00000000..322c149b --- /dev/null +++ b/app/feature/tag/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + id("diary.app.feature") +} + +kotlin { + sourceSets { + commonMain { + dependencies { + } + } + } +} + +android { + namespace = "${Build.NAMESPACE}.feature.tag" +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagNavigation.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagNavigation.kt new file mode 100644 index 00000000..fce4f59a --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagNavigation.kt @@ -0,0 +1,14 @@ +package io.github.taetae98coding.diary.feature.tag + +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import io.github.taetae98coding.diary.core.navigation.tag.TagDestination + +public fun NavGraphBuilder.tagNavigation( + navController: NavController, +) { + composable { + TagRoute() + } +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagRoute.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagRoute.kt new file mode 100644 index 00000000..2cbf32e6 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagRoute.kt @@ -0,0 +1,93 @@ +package io.github.taetae98coding.diary.feature.tag + +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.layout.AnimatedPane +import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold +import androidx.compose.material3.adaptive.layout.PaneAdaptedValue +import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole +import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import io.github.taetae98coding.diary.feature.tag.detail.TagDetailFloatingButton +import io.github.taetae98coding.diary.feature.tag.detail.TagDetailNavigationButton +import io.github.taetae98coding.diary.feature.tag.detail.TagDetailScreen +import io.github.taetae98coding.diary.feature.tag.list.TagListFloatingButton +import io.github.taetae98coding.diary.feature.tag.list.TagListScreen + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +@Composable +internal fun TagRoute( + modifier: Modifier = Modifier, +) { + val navigator = rememberListDetailPaneScaffoldNavigator() + + ListDetailPaneScaffold( + directive = navigator.scaffoldDirective, + value = navigator.scaffoldValue, + listPane = { + AnimatedPane { + val isFloatingVisible by remember { + derivedStateOf { + val isAdd = navigator.currentDestination?.content == null + val isListVisible = navigator.scaffoldValue.secondary == PaneAdaptedValue.Expanded + val isDetailVisible = navigator.scaffoldValue.primary == PaneAdaptedValue.Expanded + + isAdd && isListVisible && !isDetailVisible + } + } + + TagListScreen( + floatingButtonProvider = { + if (isFloatingVisible) { + TagListFloatingButton.Add(onAdd = { navigator.navigateTo(ThreePaneScaffoldRole.Primary) }) + } else { + TagListFloatingButton.None + } + }, + ) + } + }, + detailPane = { + val isNavigateUpVisible by remember { + derivedStateOf { + val isListVisible = navigator.scaffoldValue.secondary == PaneAdaptedValue.Expanded + val isDetailVisible = navigator.scaffoldValue.primary == PaneAdaptedValue.Expanded + + !isListVisible && isDetailVisible + } + } + + val isFloatingVisible by remember { + derivedStateOf { + val isAdd = navigator.currentDestination?.content == null + val isDetailVisible = navigator.scaffoldValue.primary == PaneAdaptedValue.Expanded + + isAdd && isDetailVisible + } + } + + AnimatedPane { + TagDetailScreen( + navigateButtonProvider = { + if (isNavigateUpVisible) { + TagDetailNavigationButton.NavigateUp(onNavigateUp = navigator::navigateBack) + } else { + TagDetailNavigationButton.None + } + }, + floatingButtonProvider = { + if (isFloatingVisible) { + TagDetailFloatingButton.Add(onAdd = { }) + } else { + TagDetailFloatingButton.None + } + }, + ) + } + }, + modifier = modifier, + ) +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailFloatingButton.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailFloatingButton.kt new file mode 100644 index 00000000..2ecc5321 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailFloatingButton.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.feature.tag.detail + +internal sealed class TagDetailFloatingButton { + data object None : TagDetailFloatingButton() + data class Add(val onAdd: () -> Unit) : TagDetailFloatingButton() +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailNavigationButton.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailNavigationButton.kt new file mode 100644 index 00000000..9bf91a40 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailNavigationButton.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.feature.tag.detail + +internal sealed class TagDetailNavigationButton { + data object None : TagDetailNavigationButton() + data class NavigateUp(val onNavigateUp: () -> Unit) : TagDetailNavigationButton() +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailScreen.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailScreen.kt new file mode 100644 index 00000000..a35da054 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailScreen.kt @@ -0,0 +1,51 @@ +package io.github.taetae98coding.diary.feature.tag.detail + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import io.github.taetae98coding.diary.core.resources.icon.AddIcon +import io.github.taetae98coding.diary.core.resources.icon.NavigateUpIcon + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun TagDetailScreen( + navigateButtonProvider: () -> TagDetailNavigationButton, + floatingButtonProvider: () -> TagDetailFloatingButton, + modifier: Modifier = Modifier, +) { + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + title = {}, + navigationIcon = { + when (val button = navigateButtonProvider()) { + is TagDetailNavigationButton.NavigateUp -> { + IconButton(onClick = button.onNavigateUp) { + NavigateUpIcon() + } + } + + is TagDetailNavigationButton.None -> Unit + } + }, + ) + }, + floatingActionButton = { + when(val button = floatingButtonProvider()) { + is TagDetailFloatingButton.Add -> { + FloatingActionButton(onClick = button.onAdd) { + AddIcon() + } + } + is TagDetailFloatingButton.None -> Unit + } + }, + ) { + + } +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListFloatingButton.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListFloatingButton.kt new file mode 100644 index 00000000..413e5b5b --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListFloatingButton.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.feature.tag.list + +internal sealed class TagListFloatingButton { + data object None : TagListFloatingButton() + data class Add(val onAdd: () -> Unit) : TagListFloatingButton() +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListScreen.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListScreen.kt new file mode 100644 index 00000000..a3c1271d --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListScreen.kt @@ -0,0 +1,51 @@ +package io.github.taetae98coding.diary.feature.tag.list + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import io.github.taetae98coding.diary.core.resources.Res +import io.github.taetae98coding.diary.core.resources.icon.AddIcon +import io.github.taetae98coding.diary.core.resources.tag +import org.jetbrains.compose.resources.stringResource + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun TagListScreen( + floatingButtonProvider: () -> TagListFloatingButton, + modifier: Modifier = Modifier, +) { + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + title = { Text(text = stringResource(Res.string.tag)) }, + ) + }, + floatingActionButton = { + when(val button = floatingButtonProvider()) { + is TagListFloatingButton.Add -> { + FloatingActionButton(onClick = button.onAdd) { + AddIcon() + } + } + is TagListFloatingButton.None -> Unit + } + }, + ) { + Box( + modifier = Modifier.fillMaxSize() + .padding(it), + contentAlignment = Alignment.Center, + ) { + Text(text = "태그 없어") + } + } +} diff --git a/app/platform/common/build.gradle.kts b/app/platform/common/build.gradle.kts index 209b0741..8ca99abf 100644 --- a/app/platform/common/build.gradle.kts +++ b/app/platform/common/build.gradle.kts @@ -27,6 +27,7 @@ kotlin { implementation(project(":app:core:holiday-service")) implementation(project(":app:feature:memo")) + implementation(project(":app:feature:tag")) implementation(project(":app:feature:calendar")) implementation(project(":app:feature:more")) implementation(project(":app:feature:account")) diff --git a/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/App.kt b/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/App.kt index efdf640b..e2cb32db 100644 --- a/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/App.kt +++ b/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/App.kt @@ -21,9 +21,11 @@ import io.github.taetae98coding.diary.core.design.system.theme.DiaryTheme import io.github.taetae98coding.diary.core.navigation.calendar.CalendarDestination import io.github.taetae98coding.diary.core.navigation.memo.MemoDestination import io.github.taetae98coding.diary.core.navigation.more.MoreDestination +import io.github.taetae98coding.diary.core.navigation.tag.TagDestination import io.github.taetae98coding.diary.core.resources.icon.CalendarIcon import io.github.taetae98coding.diary.core.resources.icon.MemoIcon import io.github.taetae98coding.diary.core.resources.icon.MoreIcon +import io.github.taetae98coding.diary.core.resources.icon.TagIcon import org.jetbrains.compose.resources.stringResource @Composable @@ -45,6 +47,7 @@ private fun AppScaffold( val isNavigationVisible by remember { derivedStateOf { val visibleDestination = listOf( + TagDestination::class, MemoDestination::class, CalendarDestination::class, MoreDestination::class, @@ -62,6 +65,7 @@ private fun AppScaffold( navigationSuiteItems = { listOf( // AppNavigation.Memo, + AppNavigation.Tag, AppNavigation.Calendar, AppNavigation.More, ).forEach { navigation -> @@ -99,5 +103,6 @@ private fun AppNavigationIcon( AppNavigation.Memo -> MemoIcon(modifier = modifier) AppNavigation.Calendar -> CalendarIcon(modifier = modifier) AppNavigation.More -> MoreIcon(modifier = modifier) + AppNavigation.Tag -> TagIcon(modifier = modifier) } } diff --git a/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/AppNavHost.kt b/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/AppNavHost.kt index 5699e549..7e11c8e6 100644 --- a/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/AppNavHost.kt +++ b/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/AppNavHost.kt @@ -9,6 +9,7 @@ import io.github.taetae98coding.diary.feature.account.accountNavigation import io.github.taetae98coding.diary.feature.calendar.calendarNavigation import io.github.taetae98coding.diary.feature.memo.memoNavigation import io.github.taetae98coding.diary.feature.more.moreNavigation +import io.github.taetae98coding.diary.feature.tag.tagNavigation @Composable internal fun AppNavHost( @@ -21,6 +22,7 @@ internal fun AppNavHost( modifier = modifier, ) { memoNavigation(navController = state.navController) + tagNavigation(navController = state.navController) calendarNavigation(navController = state.navController) moreNavigation(navController = state.navController) accountNavigation(navController = state.navController) diff --git a/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/navigation/AppNavigation.kt b/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/navigation/AppNavigation.kt index a159440e..a41d5638 100644 --- a/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/navigation/AppNavigation.kt +++ b/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/navigation/AppNavigation.kt @@ -3,10 +3,12 @@ package io.github.taetae98coding.diary.app.navigation import io.github.taetae98coding.diary.core.navigation.calendar.CalendarDestination import io.github.taetae98coding.diary.core.navigation.memo.MemoDestination import io.github.taetae98coding.diary.core.navigation.more.MoreDestination +import io.github.taetae98coding.diary.core.navigation.tag.TagDestination import io.github.taetae98coding.diary.core.resources.Res import io.github.taetae98coding.diary.core.resources.calendar import io.github.taetae98coding.diary.core.resources.memo import io.github.taetae98coding.diary.core.resources.more +import io.github.taetae98coding.diary.core.resources.tag import org.jetbrains.compose.resources.StringResource internal enum class AppNavigation( @@ -18,6 +20,11 @@ internal enum class AppNavigation( route = MemoDestination, ), + Tag( + title = Res.string.tag, + route = TagDestination, + ), + Calendar( title = Res.string.calendar, route = CalendarDestination, diff --git a/settings.gradle.kts b/settings.gradle.kts index 0031627f..5e161526 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -77,6 +77,7 @@ include(":app:domain:fcm") include(":app:domain:credential") include(":app:feature:memo") +include(":app:feature:tag") include(":app:feature:calendar") include(":app:feature:more") include(":app:feature:account")