From ce0388ad0fae085660892712d2bc76021343f773 Mon Sep 17 00:00:00 2001 From: "Jihee.Han" Date: Sun, 22 Feb 2026 20:40:41 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[NDGL-102]=20refactor:=20Splash=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=EC=97=90=20UiIntent=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/yapp/ndgl/feature/splash/SplashContract.kt | 3 +++ .../java/com/yapp/ndgl/feature/splash/SplashViewModel.kt | 7 ++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/feature/splash/src/main/java/com/yapp/ndgl/feature/splash/SplashContract.kt b/feature/splash/src/main/java/com/yapp/ndgl/feature/splash/SplashContract.kt index f96584e8..f520fb24 100644 --- a/feature/splash/src/main/java/com/yapp/ndgl/feature/splash/SplashContract.kt +++ b/feature/splash/src/main/java/com/yapp/ndgl/feature/splash/SplashContract.kt @@ -1,10 +1,13 @@ package com.yapp.ndgl.feature.splash +import com.yapp.ndgl.core.base.UiIntent import com.yapp.ndgl.core.base.UiSideEffect import com.yapp.ndgl.core.base.UiState class SplashState : UiState +sealed interface SplashUiIntent : UiIntent + sealed interface SplashSideEffect : UiSideEffect { data class NavigateToHome(val isFirstUser: Boolean) : SplashSideEffect } diff --git a/feature/splash/src/main/java/com/yapp/ndgl/feature/splash/SplashViewModel.kt b/feature/splash/src/main/java/com/yapp/ndgl/feature/splash/SplashViewModel.kt index 60218222..d90d25ee 100644 --- a/feature/splash/src/main/java/com/yapp/ndgl/feature/splash/SplashViewModel.kt +++ b/feature/splash/src/main/java/com/yapp/ndgl/feature/splash/SplashViewModel.kt @@ -2,7 +2,6 @@ package com.yapp.ndgl.feature.splash import androidx.lifecycle.viewModelScope import com.yapp.ndgl.core.base.BaseViewModel -import com.yapp.ndgl.core.base.UiIntent import com.yapp.ndgl.core.util.suspendRunCatching import com.yapp.ndgl.data.auth.repository.AuthRepository import dagger.hilt.android.lifecycle.HiltViewModel @@ -12,7 +11,7 @@ import javax.inject.Inject @HiltViewModel class SplashViewModel @Inject constructor( private val authRepository: AuthRepository, -) : BaseViewModel( +) : BaseViewModel( initialState = SplashState(), ) { init { @@ -29,7 +28,5 @@ class SplashViewModel @Inject constructor( } } - override suspend fun handleIntent(intent: UiIntent) { - // Splash에 따로 intent 존재하지 않음 - } + override suspend fun handleIntent(intent: SplashUiIntent) = Unit } From 312d7f05a5d73d6aa620cf99ecd06eba2e73d163 Mon Sep 17 00:00:00 2001 From: "Jihee.Han" Date: Sun, 22 Feb 2026 20:41:18 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[NDGL-102]=20feat:=20=EC=95=B1=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=ED=99=94=EB=A9=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/auth/repository/AuthRepository.kt | 4 +- feature/home/build.gradle.kts | 1 + .../ndgl/feature/home/main/HomeContract.kt | 2 + .../yapp/ndgl/feature/home/main/HomeScreen.kt | 9 +- .../ndgl/feature/home/main/HomeViewModel.kt | 7 +- .../ndgl/feature/home/navigation/HomeEntry.kt | 11 ++ .../feature/home/settings/SettingsContract.kt | 70 +++++++ .../feature/home/settings/SettingsScreen.kt | 174 ++++++++++++++++++ .../home/settings/SettingsViewModel.kt | 46 +++++ .../feature/home/util/AppVersionProvider.kt | 15 ++ feature/home/src/main/res/values/strings.xml | 13 +- .../travel/mytravel/MyTravelContract.kt | 2 + .../feature/travel/mytravel/MyTravelScreen.kt | 10 +- .../travel/mytravel/MyTravelViewModel.kt | 5 + .../feature/travel/navigation/TravelEntry.kt | 3 + .../java/com/yapp/ndgl/navigation/Route.kt | 3 + 16 files changed, 368 insertions(+), 7 deletions(-) create mode 100644 feature/home/src/main/java/com/yapp/ndgl/feature/home/settings/SettingsContract.kt create mode 100644 feature/home/src/main/java/com/yapp/ndgl/feature/home/settings/SettingsScreen.kt create mode 100644 feature/home/src/main/java/com/yapp/ndgl/feature/home/settings/SettingsViewModel.kt create mode 100644 feature/home/src/main/java/com/yapp/ndgl/feature/home/util/AppVersionProvider.kt diff --git a/data/auth/src/main/java/com/yapp/ndgl/data/auth/repository/AuthRepository.kt b/data/auth/src/main/java/com/yapp/ndgl/data/auth/repository/AuthRepository.kt index 8ddaa9fb..1d8c6d17 100644 --- a/data/auth/src/main/java/com/yapp/ndgl/data/auth/repository/AuthRepository.kt +++ b/data/auth/src/main/java/com/yapp/ndgl/data/auth/repository/AuthRepository.kt @@ -22,7 +22,7 @@ class AuthRepository @Inject constructor( private val localAuthDataSource: LocalAuthDataSource, ) { suspend fun initSession(): Boolean { - val uuid = localAuthDataSource.getUuid() + val uuid = getIdentifierCode() var isFirstUser = false val response = if (uuid.isNotEmpty()) { suspendRunCatching { @@ -65,4 +65,6 @@ class AuthRepository @Inject constructor( throw IllegalStateException("Failed to get FCM token", e) } } + + suspend fun getIdentifierCode(): String = localAuthDataSource.getUuid() } diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts index 106213ee..a2ea21f9 100644 --- a/feature/home/build.gradle.kts +++ b/feature/home/build.gradle.kts @@ -7,5 +7,6 @@ android { } dependencies { + implementation(project(":data:auth")) implementation(project(":data:travel")) } diff --git a/feature/home/src/main/java/com/yapp/ndgl/feature/home/main/HomeContract.kt b/feature/home/src/main/java/com/yapp/ndgl/feature/home/main/HomeContract.kt index 9fef95df..7898aea7 100644 --- a/feature/home/src/main/java/com/yapp/ndgl/feature/home/main/HomeContract.kt +++ b/feature/home/src/main/java/com/yapp/ndgl/feature/home/main/HomeContract.kt @@ -64,6 +64,7 @@ data class HomeState( sealed interface HomeIntent : UiIntent { data object ClickSearchTravelTemplate : HomeIntent + data object ClickSettings : HomeIntent data class SelectPopularTravelTab(val index: Int) : HomeIntent data class ClickTravel(val travelId: Long) : HomeIntent data object ClickTravelMore : HomeIntent @@ -71,6 +72,7 @@ sealed interface HomeIntent : UiIntent { sealed interface HomeSideEffect : UiSideEffect { data object NavigateToSearchTravelTemplate : HomeSideEffect + data object NavigateToSettings : HomeSideEffect data class NavigateToFollowTravel(val travelId: Long, val days: Int) : HomeSideEffect data object NavigateToTravelMore : HomeSideEffect } diff --git a/feature/home/src/main/java/com/yapp/ndgl/feature/home/main/HomeScreen.kt b/feature/home/src/main/java/com/yapp/ndgl/feature/home/main/HomeScreen.kt index b2a1d54e..2004d995 100644 --- a/feature/home/src/main/java/com/yapp/ndgl/feature/home/main/HomeScreen.kt +++ b/feature/home/src/main/java/com/yapp/ndgl/feature/home/main/HomeScreen.kt @@ -29,6 +29,7 @@ import java.time.LocalDate internal fun HomeRoute( viewModel: HomeViewModel = hiltViewModel(), navigateToTemplateSearch: () -> Unit, + navigateToSettings: () -> Unit, navigateToFollowTravel: (Long, Int) -> Unit, navigateToPopularTravelList: () -> Unit, ) { @@ -39,6 +40,9 @@ internal fun HomeRoute( onSearchClick = { viewModel.onIntent(HomeIntent.ClickSearchTravelTemplate) }, + onSettingsClick = { + viewModel.onIntent(HomeIntent.ClickSettings) + }, onTabSelected = { index -> viewModel.onIntent(HomeIntent.SelectPopularTravelTab(index)) }, @@ -53,6 +57,7 @@ internal fun HomeRoute( viewModel.collectSideEffect { sideEffect -> when (sideEffect) { HomeSideEffect.NavigateToSearchTravelTemplate -> navigateToTemplateSearch() + HomeSideEffect.NavigateToSettings -> navigateToSettings() is HomeSideEffect.NavigateToFollowTravel -> navigateToFollowTravel(sideEffect.travelId, sideEffect.days) HomeSideEffect.NavigateToTravelMore -> navigateToPopularTravelList() } @@ -63,6 +68,7 @@ internal fun HomeRoute( private fun HomeScreen( state: HomeState, onSearchClick: () -> Unit, + onSettingsClick: () -> Unit, onTabSelected: (Int) -> Unit, onTravelClick: (Long) -> Unit, onTravelMoreClick: () -> Unit, @@ -82,7 +88,7 @@ private fun HomeScreen( ) NDGLNavigationIcon( icon = R.drawable.ic_28_settings, - onClick = { /* FIXME: 설정 */ }, + onClick = onSettingsClick, ) }, ) @@ -204,6 +210,7 @@ private fun HomeScreenPreview() { allPopularTravels = sampleTravels, ), onSearchClick = {}, + onSettingsClick = {}, onTabSelected = {}, onTravelClick = {}, onTravelMoreClick = {}, diff --git a/feature/home/src/main/java/com/yapp/ndgl/feature/home/main/HomeViewModel.kt b/feature/home/src/main/java/com/yapp/ndgl/feature/home/main/HomeViewModel.kt index f691c159..d46d35df 100644 --- a/feature/home/src/main/java/com/yapp/ndgl/feature/home/main/HomeViewModel.kt +++ b/feature/home/src/main/java/com/yapp/ndgl/feature/home/main/HomeViewModel.kt @@ -173,11 +173,10 @@ class HomeViewModel @Inject constructor( override suspend fun handleIntent(intent: HomeIntent) { when (intent) { HomeIntent.ClickSearchTravelTemplate -> postNavigateToSearchTravelTemplate() - + HomeIntent.ClickSettings -> postNavigateToSettings() is HomeIntent.SelectPopularTravelTab -> { reduce { copy(popularTravelSelectedTabIndex = intent.index) } } - is HomeIntent.ClickTravel -> postNavigateToTravelTemplate(travelId = intent.travelId) HomeIntent.ClickTravelMore -> postNavigateToTravelMore() } @@ -187,6 +186,10 @@ class HomeViewModel @Inject constructor( postSideEffect(HomeSideEffect.NavigateToSearchTravelTemplate) } + private fun postNavigateToSettings() { + postSideEffect(HomeSideEffect.NavigateToSettings) + } + private fun postNavigateToTravelTemplate(travelId: Long) { postSideEffect(HomeSideEffect.NavigateToFollowTravel(travelId = travelId, days = 1)) } diff --git a/feature/home/src/main/java/com/yapp/ndgl/feature/home/navigation/HomeEntry.kt b/feature/home/src/main/java/com/yapp/ndgl/feature/home/navigation/HomeEntry.kt index f864fbfc..b966773e 100644 --- a/feature/home/src/main/java/com/yapp/ndgl/feature/home/navigation/HomeEntry.kt +++ b/feature/home/src/main/java/com/yapp/ndgl/feature/home/navigation/HomeEntry.kt @@ -5,6 +5,7 @@ import androidx.navigation3.runtime.NavKey import com.yapp.ndgl.feature.home.main.HomeRoute import com.yapp.ndgl.feature.home.popular.PopularTravelListRoute import com.yapp.ndgl.feature.home.search.TemplateSearchRoute +import com.yapp.ndgl.feature.home.settings.SettingsRoute import com.yapp.ndgl.navigation.Navigator import com.yapp.ndgl.navigation.Route @@ -16,6 +17,9 @@ fun EntryProviderScope.homeEntry( navigateToTemplateSearch = { navigator.navigate(Route.TemplateSearch) }, + navigateToSettings = { + navigator.navigate(Route.Settings) + }, navigateToFollowTravel = { travelId, days -> navigator.navigate(Route.FollowTravel(travelId = travelId, days = days)) }, @@ -47,4 +51,11 @@ fun EntryProviderScope.homeEntry( }, ) } + entry { + SettingsRoute( + goBack = { + navigator.goBack() + }, + ) + } } diff --git a/feature/home/src/main/java/com/yapp/ndgl/feature/home/settings/SettingsContract.kt b/feature/home/src/main/java/com/yapp/ndgl/feature/home/settings/SettingsContract.kt new file mode 100644 index 00000000..3d1e4b15 --- /dev/null +++ b/feature/home/src/main/java/com/yapp/ndgl/feature/home/settings/SettingsContract.kt @@ -0,0 +1,70 @@ +package com.yapp.ndgl.feature.home.settings + +import androidx.annotation.StringRes +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable +import com.yapp.ndgl.core.base.UiIntent +import com.yapp.ndgl.core.base.UiSideEffect +import com.yapp.ndgl.core.base.UiState +import com.yapp.ndgl.feature.home.R +import com.yapp.ndgl.feature.home.settings.SettingsState.UrlMenu +import kotlinx.collections.immutable.ImmutableList + +@Immutable +data class SettingsState( + val menuItems: ImmutableList, +) : UiState { + @Stable + sealed interface SettingsMenu { + @get:StringRes + val labelRes: Int + + data class OpenUrl( + val menu: UrlMenu, + ) : SettingsMenu { + override val labelRes = menu.labelRes + } + + data object CopyIdentifierCode : SettingsMenu { + override val labelRes = R.string.home_settings_identification_code + } + + data class AppVersion( + val versionName: String, + ) : SettingsMenu { + override val labelRes = R.string.home_settings_version_info + } + } + + enum class UrlMenu( + @get:StringRes val labelRes: Int, + val url: String, + ) { + FAQ( + labelRes = R.string.home_settings_faq, + url = "https://repeated-tapir-33f.notion.site/FAQ-30ccbdc5a38380d6af4af7b7c412921e?source=copy_link", + ), + RECOMMEND_LINK( + labelRes = R.string.home_settings_recommend_link, + url = "https://forms.gle/3q1uhQVeeKRrz11y5", + ), + TERMS_OF_SERVICE( + labelRes = R.string.home_settings_terms_of_service, + url = "https://repeated-tapir-33f.notion.site/2c8cbdc5a3838070a8d8ccdcd0631c9a?source=copy_link", + ), + PRIVACY_POLICY( + labelRes = R.string.home_settings_privacy_policy, + url = "https://repeated-tapir-33f.notion.site/30ccbdc5a38380e3a50ace64a9b0f398?source=copy_link", + ), + } +} + +sealed interface SettingsIntent : UiIntent { + data class ClickUrlMenu(val menu: UrlMenu) : SettingsIntent + data object ClickCopyIdentifierCodeMenu : SettingsIntent +} + +sealed interface SettingsSideEffect : UiSideEffect { + data class OpenUrl(val url: String) : SettingsSideEffect + data class CopyIdentifierCode(val code: String) : SettingsSideEffect +} diff --git a/feature/home/src/main/java/com/yapp/ndgl/feature/home/settings/SettingsScreen.kt b/feature/home/src/main/java/com/yapp/ndgl/feature/home/settings/SettingsScreen.kt new file mode 100644 index 00000000..13b03f73 --- /dev/null +++ b/feature/home/src/main/java/com/yapp/ndgl/feature/home/settings/SettingsScreen.kt @@ -0,0 +1,174 @@ +package com.yapp.ndgl.feature.home.settings + +import android.content.ClipData +import android.content.ClipboardManager +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.unit.dp +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel +import com.yapp.ndgl.core.ui.designsystem.NDGLNavigationBar +import com.yapp.ndgl.core.ui.designsystem.NDGLNavigationBarAttr +import com.yapp.ndgl.core.ui.theme.NDGLTheme +import com.yapp.ndgl.core.ui.util.launchBrowser +import com.yapp.ndgl.feature.home.R +import com.yapp.ndgl.feature.home.settings.SettingsState.SettingsMenu +import kotlinx.collections.immutable.ImmutableList +import com.yapp.ndgl.core.ui.R as CoreR + +@Composable +internal fun SettingsRoute( + viewModel: SettingsViewModel = hiltViewModel(), + goBack: () -> Unit, +) { + val context = LocalContext.current + + val state by viewModel.collectAsState() + + SettingsScreen( + menuItems = state.menuItems, + goBack = goBack, + onUrlItemClick = { + viewModel.onIntent(SettingsIntent.ClickUrlMenu(it)) + }, + onCopyIdentifierCodeClick = { + viewModel.onIntent(SettingsIntent.ClickCopyIdentifierCodeMenu) + }, + ) + + viewModel.collectSideEffect { sideEffect -> + when (sideEffect) { + is SettingsSideEffect.OpenUrl -> context.launchBrowser(sideEffect.url) + is SettingsSideEffect.CopyIdentifierCode -> { + val clipboard = context.getSystemService(ClipboardManager::class.java) + val clip = ClipData.newPlainText( + context.getString(R.string.home_settings_identification_code), + sideEffect.code, + ) + clipboard?.setPrimaryClip(clip) + } + } + } +} + +@Composable +private fun SettingsScreen( + menuItems: ImmutableList, + goBack: () -> Unit, + onUrlItemClick: (SettingsState.UrlMenu) -> Unit, + onCopyIdentifierCodeClick: () -> Unit, +) { + Scaffold( + topBar = { + NDGLNavigationBar( + textAlignType = NDGLNavigationBarAttr.TextAlignType.CENTER, + modifier = Modifier + .fillMaxWidth() + .background(color = NDGLTheme.colors.white) + .statusBarsPadding(), + headline = stringResource(R.string.home_settings_title), + leadingIcon = CoreR.drawable.ic_28_chevron_left, + onLeadingIconClick = goBack, + ) + }, + ) { innerPadding -> + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), + contentPadding = PaddingValues(24.dp), + ) { + items( + items = menuItems, + key = { item -> item.labelRes }, + ) { item -> + when (item) { + is SettingsMenu.OpenUrl -> SettingsMenuItem( + text = stringResource(item.labelRes), + onClick = { onUrlItemClick(item.menu) }, + ) + + SettingsMenu.CopyIdentifierCode -> SettingsMenuItem( + text = stringResource(item.labelRes), + onClick = onCopyIdentifierCodeClick, + ) + + is SettingsMenu.AppVersion -> VersionItem(versionName = item.versionName) + } + } + } + } +} + +@Composable +private fun SettingsMenuItem( + text: String, + onClick: () -> Unit, +) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable(onClick = onClick) + .padding(horizontal = 8.dp, vertical = 16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = text, + style = NDGLTheme.typography.bodyLgRegular, + color = NDGLTheme.colors.black700, + modifier = Modifier.weight(1f), + ) + Icon( + imageVector = ImageVector.vectorResource(CoreR.drawable.ic_24_chevron_right), + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = NDGLTheme.colors.black600, + ) + } + HorizontalDivider(color = NDGLTheme.colors.black50) +} + +@Composable +private fun VersionItem( + versionName: String, +) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp, vertical = 16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = stringResource(R.string.home_settings_version_info), + style = NDGLTheme.typography.bodyLgRegular, + color = NDGLTheme.colors.black700, + modifier = Modifier.weight(1f), + ) + Text( + text = versionName, + style = NDGLTheme.typography.bodyLgRegular, + color = NDGLTheme.colors.black400, + ) + } + HorizontalDivider(color = NDGLTheme.colors.black50) +} diff --git a/feature/home/src/main/java/com/yapp/ndgl/feature/home/settings/SettingsViewModel.kt b/feature/home/src/main/java/com/yapp/ndgl/feature/home/settings/SettingsViewModel.kt new file mode 100644 index 00000000..f1b8dd6a --- /dev/null +++ b/feature/home/src/main/java/com/yapp/ndgl/feature/home/settings/SettingsViewModel.kt @@ -0,0 +1,46 @@ +package com.yapp.ndgl.feature.home.settings + +import androidx.lifecycle.viewModelScope +import com.yapp.ndgl.core.base.BaseViewModel +import com.yapp.ndgl.data.auth.repository.AuthRepository +import com.yapp.ndgl.feature.home.settings.SettingsState.SettingsMenu +import com.yapp.ndgl.feature.home.util.AppVersionProvider +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.persistentListOf +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class SettingsViewModel @Inject constructor( + private val appVersionProvider: AppVersionProvider, + private val authRepository: AuthRepository, +) : BaseViewModel( + initialState = SettingsState( + menuItems = persistentListOf( + SettingsMenu.OpenUrl(SettingsState.UrlMenu.FAQ), + SettingsMenu.OpenUrl(SettingsState.UrlMenu.RECOMMEND_LINK), + SettingsMenu.CopyIdentifierCode, + SettingsMenu.OpenUrl(SettingsState.UrlMenu.TERMS_OF_SERVICE), + SettingsMenu.OpenUrl(SettingsState.UrlMenu.PRIVACY_POLICY), + SettingsMenu.AppVersion(appVersionProvider.getAppVersion()), + ), + ), +) { + override suspend fun handleIntent(intent: SettingsIntent) { + when (intent) { + is SettingsIntent.ClickUrlMenu -> postOpenUrl(intent.menu) + SettingsIntent.ClickCopyIdentifierCodeMenu -> postCopyIdentifierCode() + } + } + + private fun postOpenUrl(menu: SettingsState.UrlMenu) { + postSideEffect(SettingsSideEffect.OpenUrl(menu.url)) + } + + private fun postCopyIdentifierCode() { + viewModelScope.launch { + val uuid = authRepository.getIdentifierCode() + postSideEffect(SettingsSideEffect.CopyIdentifierCode(uuid)) + } + } +} diff --git a/feature/home/src/main/java/com/yapp/ndgl/feature/home/util/AppVersionProvider.kt b/feature/home/src/main/java/com/yapp/ndgl/feature/home/util/AppVersionProvider.kt new file mode 100644 index 00000000..7a9d05eb --- /dev/null +++ b/feature/home/src/main/java/com/yapp/ndgl/feature/home/util/AppVersionProvider.kt @@ -0,0 +1,15 @@ +package com.yapp.ndgl.feature.home.util + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject + +class AppVersionProvider @Inject constructor( + @param:ApplicationContext private val context: Context, +) { + fun getAppVersion(): String { + return runCatching { + context.packageManager.getPackageInfo(context.packageName, 0).versionName ?: "" + }.getOrDefault("") + } +} diff --git a/feature/home/src/main/res/values/strings.xml b/feature/home/src/main/res/values/strings.xml index bebd3459..a5fe0415 100644 --- a/feature/home/src/main/res/values/strings.xml +++ b/feature/home/src/main/res/values/strings.xml @@ -1,5 +1,8 @@ + + + 아직 등록된 여행지가 없어요 새 여행 일정을 만들어 보세요! @@ -26,6 +29,12 @@ 정보를 불러올 수 없어요 인터넷 연결 확인 후 다시 시도해 주세요 - - + + 설정 + FAQ + 콘텐츠 추천 링크 넣기 + 내 식별코드 + 서비스 약관 + 개인정보처리 방침 + 버전 정보 diff --git a/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelContract.kt b/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelContract.kt index 6ab903fc..6caa2c46 100644 --- a/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelContract.kt +++ b/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelContract.kt @@ -80,6 +80,7 @@ data class MyTravelState( sealed interface MyTravelIntent : UiIntent { data object ClickSearchTravelTemplate : MyTravelIntent + data object ClickSettings : MyTravelIntent data class ClickTravel(val travelId: Long) : MyTravelIntent data class ClickTravelDetail(val travelId: Long) : MyTravelIntent data class ClickPlaceDetail(val placeId: String) : MyTravelIntent @@ -88,6 +89,7 @@ sealed interface MyTravelIntent : UiIntent { sealed interface MyTravelSideEffect : UiSideEffect { data object NavigateToSearchTravelTemplate : MyTravelSideEffect + data object NavigateToSettings : MyTravelSideEffect data class NavigateToFollowTravel(val travelId: Long, val days: Int) : MyTravelSideEffect data class NavigateToTravelDetail(val travelId: Long) : MyTravelSideEffect data class NavigateToTravelPlace(val placeId: String) : MyTravelSideEffect diff --git a/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelScreen.kt b/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelScreen.kt index 671d84d1..6953bfbc 100644 --- a/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelScreen.kt +++ b/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelScreen.kt @@ -26,6 +26,7 @@ import com.yapp.ndgl.core.ui.theme.NDGLTheme internal fun MyTravelRoute( viewModel: MyTravelViewModel = hiltViewModel(), navigateToTemplateSearch: () -> Unit, + navigateToSettings: () -> Unit, navigateToFollowTravel: (Long, Int) -> Unit, navigateToTravelDetail: (Long) -> Unit, navigateToTravelPlace: (String) -> Unit, @@ -38,6 +39,9 @@ internal fun MyTravelRoute( onSearchClick = { viewModel.onIntent(MyTravelIntent.ClickSearchTravelTemplate) }, + onSettingsClick = { + viewModel.onIntent(MyTravelIntent.ClickSettings) + }, onTravelClick = { travelId -> viewModel.onIntent(MyTravelIntent.ClickTravelDetail(travelId = travelId)) }, @@ -56,6 +60,8 @@ internal fun MyTravelRoute( when (sideEffect) { MyTravelSideEffect.NavigateToSearchTravelTemplate -> navigateToTemplateSearch() + MyTravelSideEffect.NavigateToSettings -> navigateToSettings() + is MyTravelSideEffect.NavigateToFollowTravel -> navigateToFollowTravel( sideEffect.travelId, sideEffect.days, @@ -78,6 +84,7 @@ internal fun MyTravelRoute( private fun MyTravelScreen( state: MyTravelState, onSearchClick: () -> Unit, + onSettingsClick: () -> Unit, onTravelClick: (Long) -> Unit, onPlaceClick: (String) -> Unit, onNewTravelFindClick: () -> Unit, @@ -99,7 +106,7 @@ private fun MyTravelScreen( ) NDGLNavigationIcon( icon = R.drawable.ic_28_settings, - onClick = { /* FIXME: 설정 */ }, + onClick = onSettingsClick, ) }, ) @@ -152,6 +159,7 @@ private fun MyTravelScreenPreview() { MyTravelScreen( state = MyTravelState(), onSearchClick = {}, + onSettingsClick = {}, onTravelClick = {}, onPlaceClick = {}, onNewTravelFindClick = {}, diff --git a/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelViewModel.kt b/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelViewModel.kt index fafc443a..620e736d 100644 --- a/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelViewModel.kt +++ b/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/mytravel/MyTravelViewModel.kt @@ -131,6 +131,7 @@ class MyTravelViewModel @Inject constructor( override suspend fun handleIntent(intent: MyTravelIntent) { when (intent) { MyTravelIntent.ClickSearchTravelTemplate -> postNavigateToSearchTravelTemplate() + MyTravelIntent.ClickSettings -> postNavigateToSettings() is MyTravelIntent.ClickTravel -> postNavigateToFollowTravel(travelId = intent.travelId) is MyTravelIntent.ClickTravelDetail -> postNavigateToTravelDetail(travelId = intent.travelId) is MyTravelIntent.ClickPlaceDetail -> postNavigateToPlaceDetail(placeId = intent.placeId) @@ -142,6 +143,10 @@ class MyTravelViewModel @Inject constructor( postSideEffect(MyTravelSideEffect.NavigateToSearchTravelTemplate) } + private fun postNavigateToSettings() { + postSideEffect(MyTravelSideEffect.NavigateToSettings) + } + private fun postNavigateToFollowTravel(travelId: Long, days: Int = 1) { postSideEffect(MyTravelSideEffect.NavigateToFollowTravel(travelId = travelId, days = days)) } diff --git a/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/navigation/TravelEntry.kt b/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/navigation/TravelEntry.kt index 0d2560ff..cdcf6c0e 100644 --- a/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/navigation/TravelEntry.kt +++ b/feature/travel/src/main/java/com/yapp/ndgl/feature/travel/navigation/TravelEntry.kt @@ -30,6 +30,9 @@ fun EntryProviderScope.travelEntry(navigator: Navigator) { navigateToTemplateSearch = { navigator.navigate(Route.TemplateSearch) }, + navigateToSettings = { + navigator.navigate(Route.Settings) + }, navigateToFollowTravel = { travelId, days -> navigator.navigate(Route.FollowTravel(travelId, days)) }, diff --git a/navigation/src/main/java/com/yapp/ndgl/navigation/Route.kt b/navigation/src/main/java/com/yapp/ndgl/navigation/Route.kt index 01a53bc4..e00d6192 100644 --- a/navigation/src/main/java/com/yapp/ndgl/navigation/Route.kt +++ b/navigation/src/main/java/com/yapp/ndgl/navigation/Route.kt @@ -19,6 +19,9 @@ sealed interface Route : NavKey { @Serializable data object PopularTravelList : Route + @Serializable + data object Settings : Route + @Serializable data object Travel : Route