Skip to content

๐Ÿ”€ :: (#122) Implementation of Logout #126

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

Merged
merged 5 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion app/src/main/java/com/kdn/stack_knowledge/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.kdn.stack_knowledge

import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
Expand All @@ -25,6 +26,7 @@ import com.stackknowledge.login.viewmodel.AuthViewModel
import com.kdn.stack_knowledge.ui.StackKnowledgeApp
import com.stackknowledge.user.R
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.runBlocking
import remote.request.auth.LoginRequestModel
import javax.inject.Inject
import javax.inject.Named
Expand Down Expand Up @@ -72,7 +74,8 @@ class MainActivity : ComponentActivity() {
windowSizeClass = calculateWindowSizeClass(this@MainActivity),
onLoginButtonClick = {
googleSocialLogin()
}
},
onLogout = { logout() }
)
}
}
Expand Down Expand Up @@ -118,4 +121,14 @@ class MainActivity : ComponentActivity() {
}
}
}

private fun logout() {
runBlocking {
viewModel.deleteToken()
}
finish()

val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.stackknowledge.shop.navigation.shopScreen
import com.stackknowledge.shop.navigation.teacherShopRoute
import com.stackknowledge.shop.navigation.teacherShopScreen
import com.kdn.stack_knowledge.ui.StackKnowledgeAppState
import com.stackknowledge.main.navigation.mainPageRoute
import com.stackkowledge.mission.navigation.createMissionScreen
import com.stackkowledge.mission.navigation.entireMissionScreen
import com.stackkowledge.mission.navigation.navigateToEntireMission
Expand All @@ -37,6 +38,7 @@ fun StackKnowledgeNavHost(
startDestination: String = roleCheckRoute,
modifier: Modifier = Modifier,
onLoginButtonClick: () -> Unit = {},
onLogout: () -> Unit,
) {
val navController = appState.navController

Expand All @@ -46,8 +48,10 @@ fun StackKnowledgeNavHost(
modifier = modifier
) {
loginScreen(
onSuccess = navController::navigateToMain,
onLoginButtonClick = onLoginButtonClick
onSuccess = {
appState.navigateToTopLevelDestination(TopLevelDestination.MAIN)
},
onLoginButtonClick = onLoginButtonClick,
)
roleCheckScreen(
onRoleButtonClick = navController::navigateToLogin
Expand All @@ -64,13 +68,29 @@ fun StackKnowledgeNavHost(
}
} else bottomNavigationNavigate(role, navController, navType)
},
logoutSuccess = {
onLogout()
appState.navigateToTopLevelDestination(TopLevelDestination.ROLE_CHECK)
},
)
createMissionScreen(
onNavigate = { role, navType -> bottomNavigationNavigate(role, navController, navType) },
onNavigate = { role, navType ->
bottomNavigationNavigate(
role,
navController,
navType
)
},
createMissionSuccess = navController::navigateToMain
)
entireMissionScreen(
onNavigate = { role, navType -> bottomNavigationNavigate(role, navController, navType) },
onNavigate = { role, navType ->
bottomNavigationNavigate(
role,
navController,
navType
)
},
onItemClick = navController::navigateToResolveMission
)
rankingScreen(
Expand All @@ -80,16 +100,34 @@ fun StackKnowledgeNavHost(
onNavigate = { role, navType -> bottomNavigationNavigate(role, navController, navType) }
)
resolveMissionScreen(
onNavigate = { role, navType -> bottomNavigationNavigate(role, navController, navType) },
onNavigate = { role, navType ->
bottomNavigationNavigate(
role,
navController,
navType
)
},
onBackClick = navController::popBackStack,
solveMissionSuccess = navController::navigateToMain,
)
gradingAnswerScreen(
onNavigate = { role, navType -> bottomNavigationNavigate(role, navController, navType) },
onNavigate = { role, navType ->
bottomNavigationNavigate(
role,
navController,
navType
)
},
scoreMissionSuccess = navController::navigateToMain,
)
solvedMissionScreen(
onNavigate = { role, navType -> bottomNavigationNavigate(role, navController, navType) },
onNavigate = { role, navType ->
bottomNavigationNavigate(
role,
navController,
navType
)
},
onItemClick = navController::navigateToGradingAnswer
)
shopScreen(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ package com.kdn.stack_knowledge.navigation

enum class TopLevelDestination {
ROLE_CHECK,
MAIN
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.navigation.NavController
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.navOptions
import com.minstone.ui.navigation.NavigateType
import com.stackknowledge.main.navigation.mainPageRoute
import com.stackknowledge.main.navigation.navigateToMain
import com.stackknowledge.ranking.navigation.navigateToRanking
import com.stackknowledge.ranking.navigation.navigateToTeacherRanking
Expand All @@ -20,7 +21,7 @@ fun bottomNavigationNavigate(
navType: String
) {
val topLevelNavOptions = navOptions {
popUpTo(navController.graph.findStartDestination().id) {
popUpTo(mainPageRoute) {
inclusive = false
}
launchSingleTop = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ fun StackKnowledgeApp(
windowSizeClass = windowSizeClass
),
onLoginButtonClick: () -> Unit = {},
onLogout: () -> Unit,
) {
StackKnowledgeAndroidTheme { _, _ ->
StackKnowledgeNavHost(
appState = appState,
onLoginButtonClick = onLoginButtonClick
onLoginButtonClick = onLoginButtonClick,
onLogout = onLogout
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.util.trace
import androidx.navigation.NavDestination
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navOptions
import com.kdn.stack_knowledge.navigation.TopLevelDestination
import com.stackknowledge.login.navigation.navigateToLogin
import com.stackknowledge.login.navigation.navigateToRoleCheck
import com.stackknowledge.login.navigation.roleCheckRoute
import com.stackknowledge.main.navigation.navigateToMain
import kotlinx.coroutines.CoroutineScope

@Composable
Expand Down Expand Up @@ -53,4 +59,19 @@ class StackKnowledgeAppState(
get() = windowSizeClass.widthSizeClass == WindowWidthSizeClass.Compact

val topLevelDestinations: List<TopLevelDestination> = TopLevelDestination.values().asList()

fun navigateToTopLevelDestination(topLevelDestination: TopLevelDestination) {
trace("Navigation: ${topLevelDestination.name}") {
val topLevelNavOptions = navOptions {
popUpTo(navController.graph.findStartDestination().id) {
inclusive = true
}
}

when (topLevelDestination) {
TopLevelDestination.ROLE_CHECK -> navController.navigateToRoleCheck(topLevelNavOptions)
TopLevelDestination.MAIN -> navController.navigateToMain(topLevelNavOptions)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ interface AuthRepository {

suspend fun saveToken(token: LoginResponseModel)

suspend fun deleteToken()

fun getRole(): Flow<String>

fun logout(): Flow<Unit>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,23 @@ class AuthRepositoryImpl @Inject constructor(

override suspend fun saveToken(token: LoginResponseModel) {
token.let {
localDataSource.setAccessToken(it.accessToken)
localDataSource.setAccessTime(it.expiredAt)
localDataSource.setRefreshToken(it.refreshToken)
localDataSource.setRefreshTime(it.expiredAt)
localDataSource.setAuthorityInfo(it.authority.toString())
with(localDataSource) {
setAccessToken(it.accessToken)
setAccessTime(it.expiredAt)
setRefreshToken(it.refreshToken)
setRefreshTime(it.expiredAt)
setAuthorityInfo(it.authority.toString())
}
}
}

override suspend fun deleteToken() {
with(localDataSource) {
removeAccessToken()
removeRefreshToken()
removeAccessTime()
removeRefreshTime()
removeAuthorityInfo()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ interface LocalAuthDataSource {
suspend fun setAuthorityInfo(authority: String)

fun getAuthorityInfo(): Flow<String>

suspend fun removeAuthorityInfo()
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,10 @@ class LocalAuthDataSourceImpl @Inject constructor(
it[AuthPreferenceKey.AUTHORITY] = authority
}
}

override suspend fun removeAuthorityInfo() {
dataStore.edit {
it.remove(AuthPreferenceKey.AUTHORITY)
}
}
}
2 changes: 2 additions & 0 deletions core/design-system/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,6 @@
<string name="finish_score_mission">๋ฌธ์ œ ์ฑ„์ ์ด ๋๋‚˜์…จ์Šต๋‹ˆ๊นŒ?</string>
<string name="success_score_mission">๋ฌธ์ œ ์ฑ„์ ์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!</string>
<string name="discount_ordered_item">์ƒํ’ˆ์„ ์ฐจ๊ฐํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?</string>
<string name="success_logout">๋กœ๊ทธ์•„์›ƒ์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.</string>
<string name="failure_logout">๋กœ๊ทธ์•„์›ƒ์ด ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class AuthInterceptor @Inject constructor(
runBlocking {
val refreshTime = dataSource.getRefreshTime().first().replace("\"", "")
val accessTime = dataSource.getAccessTime().first().replace("\"", "")
val refreshToken = dataSource.getRefreshToken().first().replace("\"", "")
val accessToken = dataSource.getAccessToken().first().replace("\"", "")

if (refreshTime == "") {
return@runBlocking
Expand Down Expand Up @@ -82,9 +84,15 @@ class AuthInterceptor @Inject constructor(
} else throw NeedLoginException()
}
}
val accessToken = dataSource.getAccessToken().first().replace("\"", "")
if (method == "DELETE") {
builder.addHeader("RefreshToken", refreshToken)
}
builder.addHeader("Authorization", "Bearer $accessToken")
}
return chain.proceed(builder.build())
val response = chain.proceed(builder.build())
return when (response.code) {
204 -> response.newBuilder().code(200).build()
else -> response
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ internal fun LoginRoute(
onGoogleLoginButtonClicked = onGoogleLoginButtonClicked,
loginUiState = loginUiState,
viewModel = viewModel,
onLoginSuccess = onSuccess
onLoginSuccess = onSuccess,
)
}

Expand Down Expand Up @@ -129,6 +129,6 @@ private fun LoginScreen(
fun LoginScreenPre() {
LoginScreen(
onGoogleLoginButtonClicked = {},
loginUiState = LoginUiState.Loading
loginUiState = LoginUiState.Loading,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fun NavGraphBuilder.loginScreen(
composable(route = loginRoute) {
LoginRoute(
onSuccess = onSuccess,
onGoogleLoginButtonClicked = onLoginButtonClick
onGoogleLoginButtonClicked = onLoginButtonClick,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.example.common.result.asResult
import com.example.common.util.Event
import com.example.common.util.errorHandling
import com.stackknowledge.login.viewmodel.uistate.LoginUiState
import com.stackknowledge.repository.auth.AuthRepository
import com.stackknowledge.usecase.auth.SaveTokenUseCase
import com.stackknowledge.usecase.auth.LoginStudentUseCase
import com.stackknowledge.usecase.auth.LoginTeacherUseCase
Expand All @@ -25,6 +26,7 @@ class AuthViewModel @Inject constructor(
private val loginStudentUseCase: LoginStudentUseCase,
private val loginTeacherUseCase: LoginTeacherUseCase,
private val saveTokenUseCase: SaveTokenUseCase,
private val authRepository: AuthRepository,
) : ViewModel() {
private val _saveTokenRequest = MutableStateFlow<Event<Nothing>>(Event.Loading)
internal val saveTokenRequest = _saveTokenRequest.asStateFlow()
Expand Down Expand Up @@ -72,4 +74,8 @@ class AuthViewModel @Inject constructor(
_saveTokenRequest.value = it.errorHandling()
}
}

fun deleteToken() = viewModelScope.launch{
authRepository.deleteToken()
}
}
Loading
Loading