-
Notifications
You must be signed in to change notification settings - Fork 1
Navigation3 기반 앱 네비게이션 시스템 구축 및 Scaffold 기본 구조 구현 #51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
94a0147
03ceb88
4634c6b
761de3f
155e80a
8a61a35
ea548a2
b584d70
d1890c9
eaeddd8
82e7ca9
c5daeb7
6db1a4d
5131676
b9ac2ac
8f26acf
6a000be
5754714
862a0c2
7adcabf
1fd6f5f
8dfcf6f
9a01872
f5000ff
c1cf1cb
f099ab3
e539419
56e55bd
8503429
7e5f5a2
c84fee3
7b27f46
e738cb2
4f81cd6
d8e05f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| package com.team.prezel.navigation | ||
|
|
||
| import androidx.annotation.DrawableRes | ||
| import androidx.annotation.StringRes | ||
| import androidx.navigation3.runtime.NavKey | ||
| import com.team.prezel.R | ||
| import com.team.prezel.core.designsystem.icon.PrezelIcons | ||
| import com.team.prezel.feature.history.api.HistoryNavKey | ||
| import com.team.prezel.feature.home.api.HomeNavKey | ||
| import com.team.prezel.feature.profile.api.ProfileNavKey | ||
| import kotlinx.collections.immutable.ImmutableSet | ||
| import kotlinx.collections.immutable.persistentMapOf | ||
|
|
||
| data class TopLevelNavItem( | ||
| @param:DrawableRes val iconRes: Int, | ||
| @param:StringRes val titleTextId: Int, | ||
| ) | ||
|
|
||
| val HOME = TopLevelNavItem( | ||
| iconRes = PrezelIcons.Home, | ||
| titleTextId = R.string.bottom_nav_home, | ||
| ) | ||
|
|
||
| val HISTORY = TopLevelNavItem( | ||
| iconRes = PrezelIcons.Storage, | ||
| titleTextId = R.string.bottom_nav_history, | ||
| ) | ||
|
|
||
| val PROFILE = TopLevelNavItem( | ||
| iconRes = PrezelIcons.Profile, | ||
| titleTextId = R.string.bottom_nav_profile, | ||
| ) | ||
|
|
||
| val TOP_LEVEL_NAV_ITEMS = persistentMapOf( | ||
| HomeNavKey to HOME, | ||
| HistoryNavKey to HISTORY, | ||
| ProfileNavKey to PROFILE, | ||
| ) | ||
|
|
||
| val TOP_LEVEL_KEYS: ImmutableSet<NavKey> = TOP_LEVEL_NAV_ITEMS.keys | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| package com.team.prezel.ui | ||
|
|
||
| import androidx.compose.animation.core.tween | ||
| import androidx.compose.animation.fadeIn | ||
| import androidx.compose.animation.fadeOut | ||
| import androidx.compose.animation.togetherWith | ||
| import androidx.compose.foundation.layout.padding | ||
| import androidx.compose.material3.SnackbarHostState | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.CompositionLocalProvider | ||
| import androidx.compose.runtime.remember | ||
| import androidx.compose.ui.Modifier | ||
| import androidx.compose.ui.res.stringResource | ||
| import androidx.navigation3.runtime.EntryProviderScope | ||
| import androidx.navigation3.runtime.NavKey | ||
| import androidx.navigation3.runtime.entryProvider | ||
| import androidx.navigation3.ui.NavDisplay | ||
| import com.team.prezel.core.designsystem.component.PrezelNavigationScaffold | ||
| import com.team.prezel.core.designsystem.icon.DrawableIcon | ||
| import com.team.prezel.core.navigation.LocalNavigator | ||
| import com.team.prezel.core.navigation.LocalSnackbarHostState | ||
| import com.team.prezel.core.navigation.Navigator | ||
| import com.team.prezel.core.navigation.toEntries | ||
| import com.team.prezel.navigation.TOP_LEVEL_NAV_ITEMS | ||
|
|
||
| @Composable | ||
| fun PrezelApp( | ||
| appState: PrezelAppState, | ||
| entryBuilders: Set<EntryProviderScope<NavKey>.() -> Unit>, | ||
| ) { | ||
| val navigator = remember(appState.navigationState) { Navigator(appState.navigationState) } | ||
| val snackbarHostState = remember { SnackbarHostState() } | ||
|
|
||
| CompositionLocalProvider( | ||
| LocalNavigator provides navigator, | ||
| LocalSnackbarHostState provides snackbarHostState, | ||
| ) { | ||
| PrezelAppContent( | ||
| appState = appState, | ||
| entryBuilders = entryBuilders, | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| @Composable | ||
| private fun PrezelAppContent( | ||
| appState: PrezelAppState, | ||
| entryBuilders: Set<EntryProviderScope<NavKey>.() -> Unit>, | ||
| ) { | ||
| val navigator = LocalNavigator.current | ||
| val snackbarHostState = LocalSnackbarHostState.current | ||
|
|
||
| val provider = remember(entryBuilders) { | ||
| entryProvider { | ||
| entryBuilders.forEach { builder -> this.builder() } | ||
| } | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| PrezelNavigationScaffold( | ||
| showNavigationBar = appState.shouldShowNavigationBar, | ||
| snackbarHostState = snackbarHostState, | ||
| navigationItems = { | ||
| TOP_LEVEL_NAV_ITEMS.forEach { (key, item) -> | ||
| item( | ||
| selected = key == appState.navigationState.currentTopLevelKey, | ||
| onClick = { navigator.navigate(key) }, | ||
| label = stringResource(item.titleTextId), | ||
| icon = DrawableIcon(item.iconRes), | ||
| ) | ||
| } | ||
| }, | ||
| ) { padding -> | ||
| NavDisplay( | ||
| entries = appState.navigationState.toEntries(provider), | ||
| onBack = navigator::goBack, | ||
| modifier = Modifier.padding(padding), | ||
| transitionSpec = { | ||
| fadeIn(animationSpec = tween(durationMillis = 100)) togetherWith | ||
| fadeOut(animationSpec = tween(durationMillis = 100)) | ||
| }, | ||
| popTransitionSpec = { | ||
| fadeIn(animationSpec = tween(durationMillis = 100)) togetherWith | ||
| fadeOut(animationSpec = tween(durationMillis = 100)) | ||
| }, | ||
| ) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| package com.team.prezel.ui | ||
|
|
||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.Stable | ||
| import androidx.compose.runtime.remember | ||
| import androidx.compose.runtime.rememberCoroutineScope | ||
| import com.team.prezel.core.data.NetworkMonitor | ||
| import com.team.prezel.core.navigation.NavigationState | ||
| import com.team.prezel.core.navigation.rememberNavigationState | ||
| import com.team.prezel.feature.home.api.HomeNavKey | ||
| import com.team.prezel.navigation.TOP_LEVEL_KEYS | ||
| import com.team.prezel.navigation.TOP_LEVEL_NAV_ITEMS | ||
| import kotlinx.coroutines.CoroutineScope | ||
| import kotlinx.coroutines.flow.SharingStarted | ||
| import kotlinx.coroutines.flow.StateFlow | ||
| import kotlinx.coroutines.flow.map | ||
| import kotlinx.coroutines.flow.stateIn | ||
|
|
||
| @Composable | ||
| fun rememberPrezelAppState( | ||
| networkMonitor: NetworkMonitor, | ||
| coroutineScope: CoroutineScope = rememberCoroutineScope(), | ||
| ): PrezelAppState { | ||
| val navigationState = rememberNavigationState( | ||
| startKey = HomeNavKey, | ||
| topLevelKeys = TOP_LEVEL_NAV_ITEMS.keys, | ||
| ) | ||
|
|
||
| return remember( | ||
| navigationState, | ||
| coroutineScope, | ||
| networkMonitor, | ||
| ) { | ||
| PrezelAppState( | ||
| navigationState = navigationState, | ||
| coroutineScope = coroutineScope, | ||
| networkMonitor = networkMonitor, | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| @Stable | ||
| class PrezelAppState( | ||
| val navigationState: NavigationState, | ||
| coroutineScope: CoroutineScope, | ||
| networkMonitor: NetworkMonitor, | ||
| ) { | ||
| val shouldShowNavigationBar | ||
| get() = navigationState.currentKey in TOP_LEVEL_KEYS | ||
|
|
||
| val isOffline: StateFlow<Boolean> = | ||
| networkMonitor.isOnline | ||
| .map(Boolean::not) | ||
| .stateIn( | ||
| scope = coroutineScope, | ||
| started = SharingStarted.WhileSubscribed(5_000), | ||
| initialValue = false, | ||
| ) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,7 @@ | ||
| <resources> | ||
| <string name="app_name">Prezel</string> | ||
|
|
||
| <string name="bottom_nav_home">홈</string> | ||
| <string name="bottom_nav_history">히스토리</string> | ||
| <string name="bottom_nav_profile">프로필</string> | ||
| </resources> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ internal fun Project.configureAndroidCompose(commonExtension: CommonExtension<*, | |
| "implementation"(platform(bom)) | ||
| "implementation"(libs.findBundle("android-compose").get()) | ||
| "androidTestImplementation"(platform(bom)) | ||
| "androidTestImplementation"(libs.findLibrary("androidx.lifecycle.runtimeTesting").get()) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이거 뭐 때문에 필요한건가요?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. collectAsStateWithLifecycle, repeatOnLifecycle 같은 “Lifecycle 기반 수집”을 검증할 때 등등 테스트시에 필요하다고 하는데, 빼고 나중에 필요할때 추가하는게 나을까요? |
||
| "implementation"(libs.findLibrary("androidx-compose-ui-tooling-preview").get()) | ||
| "debugImplementation"(libs.findLibrary("androidx-compose-ui-tooling").get()) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,6 @@ | ||||||||||||||||
| package com.team.prezel.buildlogic.convention.plugin | ||||||||||||||||
|
|
||||||||||||||||
| import com.team.prezel.buildlogic.convention.internal.libs | ||||||||||||||||
| import org.gradle.api.Plugin | ||||||||||||||||
| import org.gradle.api.Project | ||||||||||||||||
| import org.gradle.kotlin.dsl.apply | ||||||||||||||||
|
|
@@ -12,7 +13,7 @@ class AndroidFeatureApiConventionPlugin : Plugin<Project> { | |||||||||||||||
| apply(plugin = "org.jetbrains.kotlin.plugin.serialization") | ||||||||||||||||
|
|
||||||||||||||||
| dependencies { | ||||||||||||||||
| // "api"(project(":core:navigation")) | ||||||||||||||||
| "implementation"(libs.findLibrary("androidx.navigation3.runtime").get()) | ||||||||||||||||
| } | ||||||||||||||||
|
Comment on lines
15
to
17
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # Find NavKey related files in the codebase
echo "=== Searching for NavKey definitions ==="
fd -i navkey -e kt -e java --type f
echo ""
echo "=== Searching for NavKey class/interface definitions ==="
rg -n "class NavKey|interface NavKey" --type=kt --type=java
echo ""
echo "=== Check feature API modules structure ==="
fd -type d "feature" | head -20Repository: Team-Prezel/Prezel-Android Length of output: 701 🏁 Script executed: echo "=== HistoryNavKey.kt content ==="
cat Prezel/feature/history/api/src/main/java/com/team/prezel/feature/history/api/HistoryNavKey.kt
echo ""
echo "=== HomeNavKey.kt content ==="
cat Prezel/feature/home/api/src/main/java/com/team/prezel/feature/home/api/HomeNavKey.kt
echo ""
echo "=== ProfileNavKey.kt content ==="
cat Prezel/feature/profile/api/src/main/java/com/team/prezel/feature/profile/api/ProfileNavKey.kt
echo ""
echo "=== Check all imports from androidx.navigation in feature API modules ==="
rg "import androidx.navigation" Prezel/feature/*/api/src --type-list | head -20Repository: Team-Prezel/Prezel-Android Length of output: 1183 🏁 Script executed: echo "=== Check what modules depend on feature API modules ==="
rg "implementation.*feature.*api|api.*feature.*api" Prezel --type gradle -A 2 -B 2 | head -30
echo ""
echo "=== Check gradle files in feature modules ==="
fd "build.gradle" Prezel/feature -A 5 | head -40Repository: Team-Prezel/Prezel-Android Length of output: 421 Feature API 모듈에서 Feature API 모듈의 🔧 api로 변경 제안 dependencies {
- "implementation"(libs.findLibrary("androidx.navigation3.runtime").get())
+ "api"(libs.findLibrary("androidx.navigation3.runtime").get())
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,5 +10,6 @@ android { | |
|
|
||
| dependencies { | ||
| implementation(projects.coreNetwork) | ||
|
|
||
| implementation(libs.kotlinx.coroutines.core) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <manifest> | ||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||
|
|
||
| <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | ||
| </manifest> |
Uh oh!
There was an error while loading. Please reload this page.