diff --git a/app/src/main/java/com/dev/briefing/BriefingNavHost.kt b/app/src/main/java/com/dev/briefing/BriefingNavHost.kt index 52a063f..170aec9 100644 --- a/app/src/main/java/com/dev/briefing/BriefingNavHost.kt +++ b/app/src/main/java/com/dev/briefing/BriefingNavHost.kt @@ -3,6 +3,7 @@ package com.dev.briefing import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.compose.NavHost +import store.newsbriefing.app.feature.auth.navigateToSignIn import store.newsbriefing.app.feature.auth.signInRoute import store.newsbriefing.app.feature.auth.signInScreen import store.newsbriefing.app.feature.bookmark.bookmarkScreen @@ -40,7 +41,10 @@ fun BriefingNavHost( navigateUp = appState.navController::navigateUp ) settingScreen( - showSnackbar = appState::showSnackBar + showSnackbar = appState::showSnackBar, + navigateUp = appState.navController::navigateUp, + navigateToSignIn = appState.navController::navigateToSignIn, + appVersion = BuildConfig.VERSION_NAME ) signInScreen( showSnackBar = appState::showSnackBar, diff --git a/build-logic/convention/src/main/kotlin/store/newsbriefing/app/buildlogic/config/Config.kt b/build-logic/convention/src/main/kotlin/store/newsbriefing/app/buildlogic/config/Config.kt index e3f16a5..68a4e6a 100644 --- a/build-logic/convention/src/main/kotlin/store/newsbriefing/app/buildlogic/config/Config.kt +++ b/build-logic/convention/src/main/kotlin/store/newsbriefing/app/buildlogic/config/Config.kt @@ -9,7 +9,7 @@ object Config { compileSdkVersion = 34, applicationId = "com.dev.briefing", versionCode = 1, - versionName = "1.0", + versionName = "2.0.0", nameSpace = "com.dev.briefing" ) val jvm = JvmConfig( diff --git a/core/data/src/main/java/store/newsbriefing/app/core/data/repository/MemberTokenRepository.kt b/core/data/src/main/java/store/newsbriefing/app/core/data/repository/MemberTokenRepository.kt index 75aec3b..7ff4c99 100644 --- a/core/data/src/main/java/store/newsbriefing/app/core/data/repository/MemberTokenRepository.kt +++ b/core/data/src/main/java/store/newsbriefing/app/core/data/repository/MemberTokenRepository.kt @@ -5,5 +5,6 @@ import store.newsbriefing.app.core.model.MemberToken interface MemberTokenRepository { fun getMemberToken() : Flow + suspend fun clearMemberToken() suspend fun saveMemberToken(memberId : Long, accessToken: String, refreshToken: String) } \ No newline at end of file diff --git a/core/data/src/main/java/store/newsbriefing/app/core/data/repository/impl/DefaultMemberTokenRepository.kt b/core/data/src/main/java/store/newsbriefing/app/core/data/repository/impl/DefaultMemberTokenRepository.kt index b263e47..41cfd61 100644 --- a/core/data/src/main/java/store/newsbriefing/app/core/data/repository/impl/DefaultMemberTokenRepository.kt +++ b/core/data/src/main/java/store/newsbriefing/app/core/data/repository/impl/DefaultMemberTokenRepository.kt @@ -1,7 +1,6 @@ package store.newsbriefing.app.core.data.repository.impl import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import store.newsbriefing.app.core.data.repository.MemberTokenRepository import store.newsbriefing.app.core.datastore.datasource.UserAuthTokenDataSource @@ -16,6 +15,10 @@ class DefaultMemberTokenRepository @Inject constructor(private val userAuthToken } } + override suspend fun clearMemberToken() { + userAuthTokenDataSource.clear() + } + override suspend fun saveMemberToken( memberId: Long, accessToken: String, diff --git a/feature/auth/src/main/java/store/newsbriefing/app/feature/auth/AuthNavigation.kt b/feature/auth/src/main/java/store/newsbriefing/app/feature/auth/AuthNavigation.kt index 56dbd05..f23247b 100644 --- a/feature/auth/src/main/java/store/newsbriefing/app/feature/auth/AuthNavigation.kt +++ b/feature/auth/src/main/java/store/newsbriefing/app/feature/auth/AuthNavigation.kt @@ -3,12 +3,17 @@ package store.newsbriefing.app.feature.auth import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable +import androidx.navigation.navOptions import store.newsbriefing.app.feature.auth.signIn.SignInRoute const val signInRoute = "signIn_route" fun NavController.navigateToSignIn() { - navigate(signInRoute) + navigate(signInRoute, navOptions { + popUpTo(signInRoute) { + inclusive = true + } + }) } fun NavGraphBuilder.signInScreen(showSnackBar : (String) -> Unit, navigateToHome : () -> Unit) { diff --git a/feature/setting/build.gradle.kts b/feature/setting/build.gradle.kts index e37f382..3ce59ed 100644 --- a/feature/setting/build.gradle.kts +++ b/feature/setting/build.gradle.kts @@ -9,6 +9,8 @@ android { } dependencies { + implementation(projects.core.data) + api(projects.core.model) api(projects.core.common) api(projects.core.designsystem) diff --git a/feature/setting/src/main/java/store/newsbriefing/app/feature/setting/SettingNavigation.kt b/feature/setting/src/main/java/store/newsbriefing/app/feature/setting/SettingNavigation.kt index c319962..79c8afc 100644 --- a/feature/setting/src/main/java/store/newsbriefing/app/feature/setting/SettingNavigation.kt +++ b/feature/setting/src/main/java/store/newsbriefing/app/feature/setting/SettingNavigation.kt @@ -11,13 +11,19 @@ fun NavController.navigateToSetting() { } fun NavGraphBuilder.settingScreen( - showSnackbar: (String) -> Unit + showSnackbar: (String) -> Unit, + navigateUp: () -> Unit, + navigateToSignIn: () -> Unit, + appVersion: String ) { composable( route = settingRoute ) { SettingRoute( - showSnackbar = showSnackbar + showSnackbar = showSnackbar, + navigateUp = navigateUp, + navigateToSignIn = navigateToSignIn, + appVersion = appVersion ) } } \ No newline at end of file diff --git a/feature/setting/src/main/java/store/newsbriefing/app/feature/setting/SettingScreen.kt b/feature/setting/src/main/java/store/newsbriefing/app/feature/setting/SettingScreen.kt index 6b6d796..7f94d13 100644 --- a/feature/setting/src/main/java/store/newsbriefing/app/feature/setting/SettingScreen.kt +++ b/feature/setting/src/main/java/store/newsbriefing/app/feature/setting/SettingScreen.kt @@ -1,5 +1,7 @@ package store.newsbriefing.app.feature.setting +import android.content.Intent +import android.net.Uri import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -16,42 +18,64 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.core.content.ContextCompat.startActivity +import androidx.hilt.navigation.compose.hiltViewModel +import kotlinx.coroutines.flow.collectLatest import store.newsbriefing.app.core.designsystem.theme.BriefingTheme import store.newsbriefing.app.core.designsystem.theme.Pretendard @Composable internal fun SettingRoute( - showSnackbar: (String) -> Unit + showSnackbar: (String) -> Unit, + navigateUp: () -> Unit, + navigateToSignIn: () -> Unit, + appVersion: String, + settingViewModel: SettingViewModel = hiltViewModel() ) { + LaunchedEffect(Unit) { + settingViewModel.eventFlow.collectLatest { event -> + when (event) { + is SettingEvent.ErrorOccurred -> { + showSnackbar(event.message) + } + is SettingEvent.Logout, is SettingEvent.DeleteMember -> { + navigateToSignIn() + } + } + } + } + SettingScreen( - showSnackbar = showSnackbar + showSnackbar = showSnackbar, + navigateUp = navigateUp, + logout = settingViewModel::logout, + deleteMember = settingViewModel::deleteMember, + appVersion = appVersion ) } -@Preview -@Composable -fun SettingScrrenPreview() { - BriefingTheme { - SettingScreen( - showSnackbar = {} - ) - } -} - @Composable internal fun SettingScreen( - showSnackbar: (String) -> Unit + showSnackbar: (String) -> Unit, + navigateUp: () -> Unit, + logout: () -> Unit, + deleteMember: () -> Unit, + appVersion: String ) { + val context = LocalContext.current + Column( modifier = Modifier .fillMaxSize() @@ -59,43 +83,61 @@ internal fun SettingScreen( .verticalScroll(rememberScrollState()) ) { TopBar { - + navigateUp() } - SettingTitle("구독 서비스") - SettingItem("Briefing Premium") { + SettingTitle(stringResource(id = R.string.subscription_service)) + SettingItem(stringResource(id = R.string.briefing_premium)) { } - SettingTitle("앱 정보") - AppVersionItem("2.0.0") - SettingItem("피드백 및 문의하기") { - + SettingTitle(stringResource(id = R.string.app_information)) + AppVersionItem(appVersion) + SettingItem(stringResource(id = R.string.feedback_and_inquiry)) { + val intent = + Intent(Intent.ACTION_VIEW, Uri.parse("https://forms.gle/HQXmEBkQ6wyW9jiw7")) + startActivity(context, intent, null) } - SettingItem("버전 노트") { - + SettingItem(stringResource(id = R.string.version_note)) { + val intent = Intent( + Intent.ACTION_VIEW, + Uri.parse("https://onve.notion.site/Briefing-8af692ff041c4fc6931b2fc897411e6d?pvs=4") + ) + startActivity(context, intent, null) } - SettingTitle("개인 정보 보호") - SettingItem("이용 약관") { - + SettingTitle(stringResource(id = R.string.privacy_policy)) + SettingItem(stringResource(id = R.string.terms_of_service)) { + val intent = Intent( + Intent.ACTION_VIEW, + Uri.parse("https://sites.google.com/view/brieifinguse/%ED%99%88") + ) + startActivity(context, intent, null) } - SettingItem("개인정보처리방침") { - + SettingItem(stringResource(id = R.string.data_processing_policy)) { + val intent = Intent( + Intent.ACTION_VIEW, + Uri.parse("https://sites.google.com/view/briefing-private/%ED%99%88") + ) + startActivity(context, intent, null) } - SettingItem("유의 사항") { - + SettingItem(stringResource(id = R.string.precautions)) { + val intent = Intent( + Intent.ACTION_VIEW, + Uri.parse("https://onve.notion.site/Briefing-e1cb17e2e7c54d3b9a7036b29ee9b11a?pvs=4") + ) + startActivity(context, intent, null) } - SettingTitle("회원 관리") - SettingItem("로그아웃") { - + SettingTitle(stringResource(id = R.string.user_management)) + SettingItem(stringResource(id = R.string.logout)) { + logout() } SettingItem( - "회원 탈퇴", + stringResource(id = R.string.withdrawal), BriefingTheme.colorScheme.TextRed ) { - + deleteMember() } } } @@ -124,7 +166,7 @@ private fun TopBar( modifier = Modifier .fillMaxWidth() .align(Alignment.Center), - text = "설정", + text = stringResource(id = R.string.setting_title), style = TextStyle( fontFamily = Pretendard, fontWeight = FontWeight.Medium, @@ -211,7 +253,7 @@ private fun AppVersionItem( horizontalArrangement = Arrangement.SpaceBetween ) { Text( - text ="앱 버전", + text = stringResource(id = R.string.app_version), style = TextStyle( fontFamily = Pretendard, fontWeight = FontWeight.Normal, diff --git a/feature/setting/src/main/java/store/newsbriefing/app/feature/setting/SettingViewModel.kt b/feature/setting/src/main/java/store/newsbriefing/app/feature/setting/SettingViewModel.kt new file mode 100644 index 0000000..7b1cb01 --- /dev/null +++ b/feature/setting/src/main/java/store/newsbriefing/app/feature/setting/SettingViewModel.kt @@ -0,0 +1,40 @@ +package store.newsbriefing.app.feature.setting + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import store.newsbriefing.app.core.common.util.EventFlow +import store.newsbriefing.app.core.common.util.MutableEventFlow +import store.newsbriefing.app.core.common.util.asEventFlow +import store.newsbriefing.app.core.data.repository.MemberRepository +import store.newsbriefing.app.core.data.repository.MemberTokenRepository +import javax.inject.Inject + +sealed class SettingEvent { + data class ErrorOccurred(val message: String) : SettingEvent() + data object Logout : SettingEvent() + data object DeleteMember : SettingEvent() +} + +@HiltViewModel +class SettingViewModel @Inject constructor( + private val memberRepository: MemberRepository, + private val memberTokenRepository: MemberTokenRepository +) : ViewModel() { + val eventFlow: EventFlow + get() = _eventFlow.asEventFlow() + private val _eventFlow = MutableEventFlow() + + fun logout() = viewModelScope.launch { + memberTokenRepository.clearMemberToken() + _eventFlow.emit(SettingEvent.Logout) + } + + fun deleteMember() = viewModelScope.launch { + val userId = memberTokenRepository.getMemberToken().first().memberId + memberRepository.deleteMember(userId) + _eventFlow.emit(SettingEvent.DeleteMember) + } +} \ No newline at end of file diff --git a/feature/setting/src/main/res/values/string.xml b/feature/setting/src/main/res/values/string.xml new file mode 100644 index 0000000..5b0e7b5 --- /dev/null +++ b/feature/setting/src/main/res/values/string.xml @@ -0,0 +1,17 @@ + + + 설정 + 구독 서비스 + Briefing Premium + 앱 정보 + 앱 버전 + 피드백 및 문의하기 + 버전 노트 + 개인 정보 보호 + 이용 약관 + 개인정보처리방침 + 유의 사항 + 회원 관리 + 로그아웃 + 회원 탈퇴 + \ No newline at end of file