From 386c7fc7404c0789927626b597ac9b107a38f5fc Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Fri, 4 Oct 2024 18:37:22 +0900 Subject: [PATCH 01/18] =?UTF-8?q?refactor=20LoginManager=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/login/LoginManager.kt | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt diff --git a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt new file mode 100644 index 00000000..7c41b5e8 --- /dev/null +++ b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt @@ -0,0 +1,61 @@ +package com.goalpanzi.mission_mate.feature.login + +import android.content.Context +import androidx.credentials.CredentialManager +import androidx.credentials.CustomCredential +import androidx.credentials.GetCredentialRequest +import androidx.credentials.GetCredentialResponse +import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption +import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential +import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +sealed class LoginData { + data class Success(val email : String) : LoginData() + data class Failed(val exception: Exception) : LoginData() +} + +class LoginManager ( + private val context : Context +) { + suspend fun request() : LoginData = withContext(Dispatchers.IO) { + val credentialManager = CredentialManager.create(context) + val signInWithGoogleOption: GetSignInWithGoogleOption = + GetSignInWithGoogleOption.Builder(BuildConfig.CREDENTIAL_WEB_CLIENT_ID) + .build() + + val request: GetCredentialRequest = GetCredentialRequest.Builder() + .addCredentialOption(signInWithGoogleOption) + .build() + + return@withContext try { + val result = credentialManager.getCredential(context, request) + handleSignIn(result) + } catch (e: Exception) { + e.printStackTrace() + LoginData.Failed(e) + } + } + + private fun handleSignIn(response: GetCredentialResponse) : LoginData { + return when (val credential = response.credential) { + is CustomCredential -> { + if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) { + try { + val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data) + LoginData.Success(googleIdTokenCredential.id) + } catch (e: GoogleIdTokenParsingException) { + LoginData.Failed(e) + } + }else { + LoginData.Failed(RuntimeException("Credential Type not equals TYPE_GOOGLE_ID_TOKEN_CREDENTIAL")) + } + } + + else -> { + LoginData.Failed(RuntimeException("Credential is not CustomCredential")) + } + } + } +} From 792c655551cbbe42bf37cab7f2bb8ae59531a08b Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Fri, 4 Oct 2024 18:39:01 +0900 Subject: [PATCH 02/18] =?UTF-8?q?refactor=20LoginViewModel=20context=20?= =?UTF-8?q?=EC=B0=B8=EC=A1=B0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mission_mate/feature/login/LoginScreen.kt | 23 ++++++++-- .../feature/login/LoginViewModel.kt | 46 ++++--------------- 2 files changed, 27 insertions(+), 42 deletions(-) diff --git a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginScreen.kt b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginScreen.kt index c7f9c1fe..51948f04 100644 --- a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginScreen.kt +++ b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginScreen.kt @@ -11,13 +11,14 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -33,16 +34,18 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.goalpanzi.mission_mate.core.designsystem.theme.ColorWhite_FFFFFFFF import com.goalpanzi.mission_mate.core.designsystem.theme.Color_FFFF5632 import com.goalpanzi.mission_mate.core.designsystem.theme.MissionMateTypography +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch @Composable fun LoginRoute( onLoginSuccess: (Boolean) -> Unit, modifier: Modifier = Modifier, + coroutineScope : CoroutineScope = rememberCoroutineScope(), + loginManager: LoginManager = rememberLoginManager(), viewModel: LoginViewModel = hiltViewModel() ) { - val context = LocalContext.current - LaunchedEffect(true) { viewModel.eventFlow.collectLatest { when (it) { @@ -54,10 +57,22 @@ fun LoginRoute( LoginScreen( modifier = modifier, - onGoogleLoginClick = { viewModel.request(context) } + onGoogleLoginClick = { + coroutineScope.launch { + viewModel.login(loginManager.request()) + } + } ) } +@Composable +private fun rememberLoginManager(): LoginManager { + val context = LocalContext.current + return remember { + LoginManager(context) + } +} + @Composable fun LoginScreen( modifier: Modifier = Modifier, diff --git a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt index b1fb9526..143639c2 100644 --- a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt +++ b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt @@ -1,16 +1,8 @@ package com.goalpanzi.mission_mate.feature.login -import android.content.Context -import androidx.credentials.CredentialManager -import androidx.credentials.CustomCredential -import androidx.credentials.GetCredentialRequest -import androidx.credentials.GetCredentialResponse import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.goalpanzi.mission_mate.core.domain.auth.usecase.LoginUseCase -import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption -import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential -import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow @@ -25,33 +17,12 @@ class LoginViewModel @Inject constructor( private val _eventFlow = MutableSharedFlow() val eventFlow = _eventFlow.asSharedFlow() - fun request(context: Context) { - viewModelScope.launch { - val credentialManager = CredentialManager.create(context) - val signInWithGoogleOption: GetSignInWithGoogleOption = - GetSignInWithGoogleOption.Builder(BuildConfig.CREDENTIAL_WEB_CLIENT_ID) - .build() - - val request: GetCredentialRequest = GetCredentialRequest.Builder() - .addCredentialOption(signInWithGoogleOption) - .build() - - try { - val result = credentialManager.getCredential(context, request) - handleSignIn(result) - } catch (e: Exception) { - e.printStackTrace() - } - } - } - - private suspend fun handleSignIn(response: GetCredentialResponse) { - when (val credential = response.credential) { - is CustomCredential -> { - if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) { + fun login(loginData: LoginData){ + when(loginData){ + is LoginData.Success -> { + viewModelScope.launch { try { - val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data) - val result = loginUseCase.requestGoogleLogin(email = googleIdTokenCredential.id) + val result = loginUseCase.requestGoogleLogin(email = loginData.email) _eventFlow.emit( result?.let { LoginEvent.Success(it.isProfileSet) @@ -59,14 +30,13 @@ class LoginViewModel @Inject constructor( LoginEvent.Error } ) - } catch (e: GoogleIdTokenParsingException) { + }catch (e: Exception){ e.printStackTrace() } } } - - else -> { - // TODO : error event + is LoginData.Failed -> { + loginData.exception.printStackTrace() } } } From 5a0e8f56d0f405122c32d82c5cdb8ea763c19457 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Mon, 7 Oct 2024 19:19:30 +0900 Subject: [PATCH 03/18] =?UTF-8?q?refactor:=20Exception=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/goalpanzi/mission_mate/feature/login/LoginManager.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt index 7c41b5e8..1988505c 100644 --- a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt +++ b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt @@ -49,12 +49,12 @@ class LoginManager ( LoginData.Failed(e) } }else { - LoginData.Failed(RuntimeException("Credential Type not equals TYPE_GOOGLE_ID_TOKEN_CREDENTIAL")) + LoginData.Failed(RuntimeException("NoDataException")) } } else -> { - LoginData.Failed(RuntimeException("Credential is not CustomCredential")) + LoginData.Failed(IllegalStateException()) } } } From ead8c35a295181a034888fc1519a9dcb694263f9 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Thu, 10 Oct 2024 23:01:33 +0900 Subject: [PATCH 04/18] =?UTF-8?q?feat:=20TokenReissueInterceptor=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interceptor/TokenReissueInterceptor.kt | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 core/network/src/main/java/com/goalpanzi/mission_mate/core/network/interceptor/TokenReissueInterceptor.kt diff --git a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/interceptor/TokenReissueInterceptor.kt b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/interceptor/TokenReissueInterceptor.kt new file mode 100644 index 00000000..35708578 --- /dev/null +++ b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/interceptor/TokenReissueInterceptor.kt @@ -0,0 +1,33 @@ +package com.goalpanzi.mission_mate.core.network.interceptor + +import com.goalpanzi.mission_mate.core.network.TokenProvider +import kotlinx.coroutines.runBlocking +import okhttp3.Interceptor +import okhttp3.Response +import java.net.HttpURLConnection +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class TokenReissueInterceptor @Inject constructor( + private val tokenProvider: TokenProvider +) : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val newRequest = chain.request().newBuilder().apply { + runBlocking { + val token = tokenProvider.getAccessToken() + token?.let { + addHeader("Authorization", "Bearer $it") + } + } + } + val response = chain.proceed(newRequest.build()) + + when (response.code) { + HttpURLConnection.HTTP_UNAUTHORIZED -> { + return chain.proceed(newRequest.build()) + } + } + return chain.proceed(newRequest.build()) + } +} From a5315a0252bc14fdb6cd816d50eee33088e4ea24 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Thu, 10 Oct 2024 23:03:33 +0900 Subject: [PATCH 05/18] =?UTF-8?q?refactor:=20NetworkModule,=20ServiceModul?= =?UTF-8?q?e=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/network/di/NetworkModule.kt | 58 +++++++++++++++++-- .../core/network/di/ServiceModule.kt | 50 +++++----------- .../{ => interceptor}/TokenInterceptor.kt | 3 +- 3 files changed, 68 insertions(+), 43 deletions(-) rename core/network/src/main/java/com/goalpanzi/mission_mate/core/network/{ => interceptor}/TokenInterceptor.kt (95%) diff --git a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/di/NetworkModule.kt b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/di/NetworkModule.kt index 63a96c09..df7cfcd2 100644 --- a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/di/NetworkModule.kt +++ b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/di/NetworkModule.kt @@ -1,7 +1,8 @@ package com.goalpanzi.mission_mate.core.network.di import com.goalpanzi.mission_mate.core.network.BuildConfig -import com.goalpanzi.mission_mate.core.network.TokenInterceptor +import com.goalpanzi.mission_mate.core.network.interceptor.TokenInterceptor +import com.goalpanzi.mission_mate.core.network.interceptor.TokenReissueInterceptor import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -13,8 +14,25 @@ import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Converter import retrofit2.Retrofit import retrofit2.converter.kotlinx.serialization.asConverterFactory +import javax.inject.Qualifier import javax.inject.Singleton +@Retention(AnnotationRetention.RUNTIME) +@Qualifier +annotation class TokenInterceptorHttpClient + +@Retention(AnnotationRetention.RUNTIME) +@Qualifier +annotation class TokenReissueInterceptorHttpClient + +@Retention(AnnotationRetention.RUNTIME) +@Qualifier +annotation class TokenRetrofit + +@Retention(AnnotationRetention.RUNTIME) +@Qualifier +annotation class TokenReissueRetrofit + @Module @InstallIn(SingletonComponent::class) internal object NetworkModule { @@ -25,19 +43,32 @@ internal object NetworkModule { return HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } } + @TokenInterceptorHttpClient @Provides @Singleton - fun provideOkhttpClient( + fun provideTokenInterceptorHttpClient( httpLoggingInterceptor: HttpLoggingInterceptor, tokenInterceptor: TokenInterceptor ): OkHttpClient { - return OkHttpClient.Builder() .addInterceptor(tokenInterceptor) .addInterceptor(httpLoggingInterceptor) .build() } + @TokenReissueInterceptorHttpClient + @Provides + @Singleton + fun provideTokenReissueInterceptorHttpClient( + httpLoggingInterceptor: HttpLoggingInterceptor, + tokenReissueInterceptor: TokenReissueInterceptor + ): OkHttpClient { + return OkHttpClient.Builder() + .addInterceptor(tokenReissueInterceptor) + .addInterceptor(httpLoggingInterceptor) + .build() + } + @Provides @Singleton fun provideJson(): Json = Json { @@ -53,10 +84,25 @@ internal object NetworkModule { return json.asConverterFactory("application/json".toMediaType()) } + @TokenRetrofit + @Provides + @Singleton + fun provideTokenRetrofit( + @TokenInterceptorHttpClient okHttpClient: OkHttpClient, + converterFactory: Converter.Factory + ) : Retrofit { + return Retrofit.Builder() + .client(okHttpClient) + .baseUrl(BuildConfig.BASE_URL) + .addConverterFactory(converterFactory) + .build() + } + + @TokenReissueRetrofit @Provides @Singleton - fun provideRetrofit( - okHttpClient: OkHttpClient, + fun provideTokenReissueRetrofit( + @TokenReissueInterceptorHttpClient okHttpClient: OkHttpClient, converterFactory: Converter.Factory ) : Retrofit { return Retrofit.Builder() @@ -65,4 +111,4 @@ internal object NetworkModule { .addConverterFactory(converterFactory) .build() } -} \ No newline at end of file +} diff --git a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/di/ServiceModule.kt b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/di/ServiceModule.kt index 53b7f742..1453a864 100644 --- a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/di/ServiceModule.kt +++ b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/di/ServiceModule.kt @@ -1,7 +1,5 @@ package com.goalpanzi.mission_mate.core.network.di -import com.goalpanzi.mission_mate.core.network.BuildConfig -import com.goalpanzi.mission_mate.core.network.TokenProvider import com.goalpanzi.mission_mate.core.network.service.LoginService import com.goalpanzi.mission_mate.core.network.service.MissionService import com.goalpanzi.mission_mate.core.network.service.OnboardingService @@ -11,11 +9,6 @@ import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.runBlocking -import okhttp3.Interceptor -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Converter import retrofit2.Retrofit import javax.inject.Singleton @@ -25,56 +18,41 @@ object ServiceModule { @Provides @Singleton - fun provideLoginService(retrofit: Retrofit): LoginService { + fun provideLoginService( + @TokenRetrofit retrofit: Retrofit + ): LoginService { return retrofit.create(LoginService::class.java) } @Provides @Singleton - fun provideProfileService(retrofit: Retrofit): ProfileService { + fun provideProfileService( + @TokenRetrofit retrofit: Retrofit + ): ProfileService { return retrofit.create(ProfileService::class.java) } @Provides @Singleton - fun provideOnboardingService(retrofit: Retrofit): OnboardingService { + fun provideOnboardingService( + @TokenRetrofit retrofit: Retrofit + ): OnboardingService { return retrofit.create(OnboardingService::class.java) } @Provides @Singleton - fun provideMissionService(retrofit: Retrofit): MissionService { + fun provideMissionService( + @TokenRetrofit retrofit: Retrofit) + : MissionService { return retrofit.create(MissionService::class.java) } @Provides @Singleton fun provideTokenService( - httpLoggingInterceptor: HttpLoggingInterceptor, - converterFactory: Converter.Factory, - tokenProvider: TokenProvider - ): TokenService { - val tokenReissueInterceptor = Interceptor { chain -> - val newRequest = chain.request().newBuilder().apply { - runBlocking { - val token = tokenProvider.getAccessToken() - token?.let { - addHeader("Authorization", "Bearer $it") - } - } - } - chain.proceed(newRequest.build()) - } - val retrofit = Retrofit.Builder() - .client( - OkHttpClient.Builder() - .addInterceptor(tokenReissueInterceptor) - .addInterceptor(httpLoggingInterceptor) - .build() - ) - .baseUrl(BuildConfig.BASE_URL) - .addConverterFactory(converterFactory) - .build() + @TokenReissueRetrofit retrofit: Retrofit + ) : TokenService { return retrofit.create(TokenService::class.java) } } diff --git a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/TokenInterceptor.kt b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/interceptor/TokenInterceptor.kt similarity index 95% rename from core/network/src/main/java/com/goalpanzi/mission_mate/core/network/TokenInterceptor.kt rename to core/network/src/main/java/com/goalpanzi/mission_mate/core/network/interceptor/TokenInterceptor.kt index 6e1cc28f..92325e21 100644 --- a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/TokenInterceptor.kt +++ b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/interceptor/TokenInterceptor.kt @@ -1,5 +1,6 @@ -package com.goalpanzi.mission_mate.core.network +package com.goalpanzi.mission_mate.core.network.interceptor +import com.goalpanzi.mission_mate.core.network.TokenProvider import com.goalpanzi.mission_mate.core.network.model.request.TokenReissueRequest import com.goalpanzi.mission_mate.core.network.service.TokenService import kotlinx.coroutines.CoroutineScope From 8c73121e5429030a9d1724cbff12c4a57d3da004 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Thu, 10 Oct 2024 23:09:05 +0900 Subject: [PATCH 06/18] =?UTF-8?q?feat:=20=20TokenExpirationHandler=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/data/auth/AuthTokenExpirationHandler.kt | 13 +++++++++++++ .../core/data/auth/di/AuthDataModule.kt | 5 +++++ .../core/network/TokenExpirationHandler.kt | 5 +++++ .../network/interceptor/TokenReissueInterceptor.kt | 5 ++++- 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 core/data/auth/src/main/java/com/goalpanzi/mission_mate/core/data/auth/AuthTokenExpirationHandler.kt create mode 100644 core/network/src/main/java/com/goalpanzi/mission_mate/core/network/TokenExpirationHandler.kt diff --git a/core/data/auth/src/main/java/com/goalpanzi/mission_mate/core/data/auth/AuthTokenExpirationHandler.kt b/core/data/auth/src/main/java/com/goalpanzi/mission_mate/core/data/auth/AuthTokenExpirationHandler.kt new file mode 100644 index 00000000..ebaa171f --- /dev/null +++ b/core/data/auth/src/main/java/com/goalpanzi/mission_mate/core/data/auth/AuthTokenExpirationHandler.kt @@ -0,0 +1,13 @@ +package com.goalpanzi.mission_mate.core.data.auth + +import com.goalpanzi.mission_mate.core.datastore.datasource.AuthDataSource +import com.goalpanzi.mission_mate.core.network.TokenExpirationHandler +import javax.inject.Inject + +class AuthTokenExpirationHandler @Inject constructor( + +) : TokenExpirationHandler { + override fun handleRefreshTokenExpiration() { + + } +} diff --git a/core/data/auth/src/main/java/com/goalpanzi/mission_mate/core/data/auth/di/AuthDataModule.kt b/core/data/auth/src/main/java/com/goalpanzi/mission_mate/core/data/auth/di/AuthDataModule.kt index 9f6053ea..a1688be3 100644 --- a/core/data/auth/src/main/java/com/goalpanzi/mission_mate/core/data/auth/di/AuthDataModule.kt +++ b/core/data/auth/src/main/java/com/goalpanzi/mission_mate/core/data/auth/di/AuthDataModule.kt @@ -1,8 +1,10 @@ package com.goalpanzi.mission_mate.core.data.auth.di +import com.goalpanzi.mission_mate.core.data.auth.AuthTokenExpirationHandler import com.goalpanzi.mission_mate.core.data.auth.AuthTokenProvider import com.goalpanzi.mission_mate.core.data.auth.repository.AuthRepositoryImpl import com.goalpanzi.mission_mate.core.domain.auth.repository.AuthRepository +import com.goalpanzi.mission_mate.core.network.TokenExpirationHandler import com.goalpanzi.mission_mate.core.network.TokenProvider import dagger.Binds import dagger.Module @@ -18,4 +20,7 @@ internal abstract class AuthDataModule { @Binds abstract fun bindTokenProvider(impl : AuthTokenProvider) : TokenProvider + + @Binds + abstract fun bindTokenExpirationHandler(impl : AuthTokenExpirationHandler) : TokenExpirationHandler } diff --git a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/TokenExpirationHandler.kt b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/TokenExpirationHandler.kt new file mode 100644 index 00000000..fb3c3f7b --- /dev/null +++ b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/TokenExpirationHandler.kt @@ -0,0 +1,5 @@ +package com.goalpanzi.mission_mate.core.network + +interface TokenExpirationHandler { + fun handleRefreshTokenExpiration() +} diff --git a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/interceptor/TokenReissueInterceptor.kt b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/interceptor/TokenReissueInterceptor.kt index 35708578..66f42fe4 100644 --- a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/interceptor/TokenReissueInterceptor.kt +++ b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/interceptor/TokenReissueInterceptor.kt @@ -1,5 +1,6 @@ package com.goalpanzi.mission_mate.core.network.interceptor +import com.goalpanzi.mission_mate.core.network.TokenExpirationHandler import com.goalpanzi.mission_mate.core.network.TokenProvider import kotlinx.coroutines.runBlocking import okhttp3.Interceptor @@ -10,7 +11,8 @@ import javax.inject.Singleton @Singleton class TokenReissueInterceptor @Inject constructor( - private val tokenProvider: TokenProvider + private val tokenProvider: TokenProvider, + private val tokenExpirationHandler : TokenExpirationHandler ) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val newRequest = chain.request().newBuilder().apply { @@ -25,6 +27,7 @@ class TokenReissueInterceptor @Inject constructor( when (response.code) { HttpURLConnection.HTTP_UNAUTHORIZED -> { + tokenExpirationHandler.handleRefreshTokenExpiration() return chain.proceed(newRequest.build()) } } From b3c43fcfc89d6a2c5cc6a27a04ea1fff151c1fb0 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Fri, 11 Oct 2024 01:15:42 +0900 Subject: [PATCH 07/18] =?UTF-8?q?chore:=20dependency=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/data/auth/build.gradle.kts | 1 + core/navigation/build.gradle.kts | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/core/data/auth/build.gradle.kts b/core/data/auth/build.gradle.kts index 0446d253..4cfb46bd 100644 --- a/core/data/auth/build.gradle.kts +++ b/core/data/auth/build.gradle.kts @@ -50,4 +50,5 @@ dependencies { implementation(project(":core:domain:auth")) implementation(project(":core:network")) implementation(project(":core:datastore")) + implementation(project(":core:navigation")) } diff --git a/core/navigation/build.gradle.kts b/core/navigation/build.gradle.kts index 9a78be5f..68363ad9 100644 --- a/core/navigation/build.gradle.kts +++ b/core/navigation/build.gradle.kts @@ -4,6 +4,8 @@ plugins { alias(libs.plugins.android.library) alias(libs.plugins.jetbrains.kotlin.android) alias(libs.plugins.kotlin.plugin.serialization) + alias(libs.plugins.kotlin.ksp) + alias(libs.plugins.hilt.android) } android { @@ -37,4 +39,7 @@ android { dependencies { implementation(libs.kotlin.serialization.json) -} \ No newline at end of file + implementation(libs.bundles.coroutines) + implementation(libs.hilt.android) + ksp(libs.hilt.compiler) +} From 9b1b66d909e9a0e17fe526abea00c6392cb379f4 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Fri, 11 Oct 2024 01:16:00 +0900 Subject: [PATCH 08/18] =?UTF-8?q?feat:=20NavigationEventHandler=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../navigation/AuthNavigationEventHandler.kt | 16 ++++++++++++ .../core/navigation/NavigationEventHandler.kt | 9 +++++++ .../core/navigation/di/NavigationModule.kt | 26 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 core/navigation/src/main/java/com/goalpanzi/mission_mate/core/navigation/AuthNavigationEventHandler.kt create mode 100644 core/navigation/src/main/java/com/goalpanzi/mission_mate/core/navigation/NavigationEventHandler.kt create mode 100644 core/navigation/src/main/java/com/goalpanzi/mission_mate/core/navigation/di/NavigationModule.kt diff --git a/core/navigation/src/main/java/com/goalpanzi/mission_mate/core/navigation/AuthNavigationEventHandler.kt b/core/navigation/src/main/java/com/goalpanzi/mission_mate/core/navigation/AuthNavigationEventHandler.kt new file mode 100644 index 00000000..671a9acb --- /dev/null +++ b/core/navigation/src/main/java/com/goalpanzi/mission_mate/core/navigation/AuthNavigationEventHandler.kt @@ -0,0 +1,16 @@ +package com.goalpanzi.mission_mate.core.navigation + +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import javax.inject.Inject + +class AuthNavigationEventHandler @Inject constructor() : NavigationEventHandler { + + private val _routeToNavigate = MutableSharedFlow() + override val routeToNavigate: SharedFlow = _routeToNavigate.asSharedFlow() + + override suspend fun triggerRouteToNavigate(route: String) { + _routeToNavigate.emit(route) + } +} diff --git a/core/navigation/src/main/java/com/goalpanzi/mission_mate/core/navigation/NavigationEventHandler.kt b/core/navigation/src/main/java/com/goalpanzi/mission_mate/core/navigation/NavigationEventHandler.kt new file mode 100644 index 00000000..34d724d5 --- /dev/null +++ b/core/navigation/src/main/java/com/goalpanzi/mission_mate/core/navigation/NavigationEventHandler.kt @@ -0,0 +1,9 @@ +package com.goalpanzi.mission_mate.core.navigation + +import kotlinx.coroutines.flow.SharedFlow + +interface NavigationEventHandler { + val routeToNavigate : SharedFlow + + suspend fun triggerRouteToNavigate(route : String) +} diff --git a/core/navigation/src/main/java/com/goalpanzi/mission_mate/core/navigation/di/NavigationModule.kt b/core/navigation/src/main/java/com/goalpanzi/mission_mate/core/navigation/di/NavigationModule.kt new file mode 100644 index 00000000..fba3ee25 --- /dev/null +++ b/core/navigation/src/main/java/com/goalpanzi/mission_mate/core/navigation/di/NavigationModule.kt @@ -0,0 +1,26 @@ +package com.goalpanzi.mission_mate.core.navigation.di + +import com.goalpanzi.mission_mate.core.navigation.AuthNavigationEventHandler +import com.goalpanzi.mission_mate.core.navigation.NavigationEventHandler +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Qualifier +import javax.inject.Singleton + +@Retention(AnnotationRetention.RUNTIME) +@Qualifier +annotation class AuthNavigation + +@Module +@InstallIn(SingletonComponent::class) +abstract class NavigationModule { + + @AuthNavigation + @Binds + @Singleton + abstract fun bindAuthNavigationEventHandler( + impl : AuthNavigationEventHandler + ) : NavigationEventHandler +} From 011bda6cdaa72f50b82facb8fe06dd3b6b0dbe8b Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Fri, 11 Oct 2024 01:16:29 +0900 Subject: [PATCH 09/18] =?UTF-8?q?feat:=20TokenReissueInterceptor=20-=20401?= =?UTF-8?q?=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/auth/AuthTokenExpirationHandler.kt | 21 +++++++++++++++++-- .../core/network/TokenExpirationHandler.kt | 2 +- .../interceptor/TokenReissueInterceptor.kt | 8 ++++--- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/core/data/auth/src/main/java/com/goalpanzi/mission_mate/core/data/auth/AuthTokenExpirationHandler.kt b/core/data/auth/src/main/java/com/goalpanzi/mission_mate/core/data/auth/AuthTokenExpirationHandler.kt index ebaa171f..e18ba0f4 100644 --- a/core/data/auth/src/main/java/com/goalpanzi/mission_mate/core/data/auth/AuthTokenExpirationHandler.kt +++ b/core/data/auth/src/main/java/com/goalpanzi/mission_mate/core/data/auth/AuthTokenExpirationHandler.kt @@ -1,13 +1,30 @@ package com.goalpanzi.mission_mate.core.data.auth import com.goalpanzi.mission_mate.core.datastore.datasource.AuthDataSource +import com.goalpanzi.mission_mate.core.datastore.datasource.DefaultDataSource +import com.goalpanzi.mission_mate.core.datastore.datasource.MissionDataSource +import com.goalpanzi.mission_mate.core.navigation.NavigationEventHandler +import com.goalpanzi.mission_mate.core.navigation.di.AuthNavigation import com.goalpanzi.mission_mate.core.network.TokenExpirationHandler +import kotlinx.coroutines.flow.collect import javax.inject.Inject class AuthTokenExpirationHandler @Inject constructor( - + private val authDataSource: AuthDataSource, + private val defaultDataSource: DefaultDataSource, + private val missionDataSource: MissionDataSource, + @AuthNavigation private val authNavigationEventHandler: NavigationEventHandler ) : TokenExpirationHandler { - override fun handleRefreshTokenExpiration() { + override suspend fun handleRefreshTokenExpiration() { + clearCachedData() + authNavigationEventHandler.triggerRouteToNavigate("RouteModel.Login") + } + + private suspend fun clearCachedData(){ + authDataSource.setAccessToken("") + authDataSource.setRefreshToken("") + defaultDataSource.clearUserData().collect() + missionDataSource.clearMissionData().collect() } } diff --git a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/TokenExpirationHandler.kt b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/TokenExpirationHandler.kt index fb3c3f7b..58bbc854 100644 --- a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/TokenExpirationHandler.kt +++ b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/TokenExpirationHandler.kt @@ -1,5 +1,5 @@ package com.goalpanzi.mission_mate.core.network interface TokenExpirationHandler { - fun handleRefreshTokenExpiration() + suspend fun handleRefreshTokenExpiration() } diff --git a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/interceptor/TokenReissueInterceptor.kt b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/interceptor/TokenReissueInterceptor.kt index 66f42fe4..533b79ec 100644 --- a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/interceptor/TokenReissueInterceptor.kt +++ b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/interceptor/TokenReissueInterceptor.kt @@ -27,10 +27,12 @@ class TokenReissueInterceptor @Inject constructor( when (response.code) { HttpURLConnection.HTTP_UNAUTHORIZED -> { - tokenExpirationHandler.handleRefreshTokenExpiration() - return chain.proceed(newRequest.build()) + runBlocking { + tokenExpirationHandler.handleRefreshTokenExpiration() + } + return response } } - return chain.proceed(newRequest.build()) + return response } } From e0fb3f66d7ed95aa83573c6ec01d4ffa43e359a7 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Fri, 11 Oct 2024 01:16:54 +0900 Subject: [PATCH 10/18] =?UTF-8?q?feat:=20Main=20-=20401=20=EC=8B=9C=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=99=94=EB=A9=B4=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../goalpanzi/mission_mate/core/main/MainScreen.kt | 14 +++++++++++--- .../mission_mate/core/main/MainViewModel.kt | 14 ++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainViewModel.kt diff --git a/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt b/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt index b79ffa7e..50fe94ee 100644 --- a/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt +++ b/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt @@ -2,17 +2,25 @@ package com.goalpanzi.mission_mate.core.main import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier +import androidx.hilt.navigation.compose.hiltViewModel import com.goalpanzi.mission_mate.core.main.component.MainNavHost import com.goalpanzi.mission_mate.core.main.component.MainNavigator import com.goalpanzi.mission_mate.core.main.component.rememberMainNavigator -import com.goalpanzi.mission_mate.core.navigation.RouteModel @Composable internal fun MainScreen( + startDestination: String, navigator: MainNavigator = rememberMainNavigator(), - startDestination: String + viewModel: MainViewModel = hiltViewModel() ) { + LaunchedEffect(Unit) { + viewModel.navigationEvent.collect { + navigator.navController.navigate(it) + } + } + MainScreenContent( navigator = navigator, startDestination = startDestination @@ -35,4 +43,4 @@ private fun MainScreenContent( ) } ) -} \ No newline at end of file +} diff --git a/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainViewModel.kt b/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainViewModel.kt new file mode 100644 index 00000000..6b1d7898 --- /dev/null +++ b/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainViewModel.kt @@ -0,0 +1,14 @@ +package com.goalpanzi.mission_mate.core.main + +import androidx.lifecycle.ViewModel +import com.goalpanzi.mission_mate.core.navigation.NavigationEventHandler +import com.goalpanzi.mission_mate.core.navigation.di.AuthNavigation +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class MainViewModel @Inject constructor( + @AuthNavigation navigationEventHandler : NavigationEventHandler +) : ViewModel() { + val navigationEvent = navigationEventHandler.routeToNavigate +} From 9267cfe0594a946481370e539639f0e83d7ac988 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Sat, 12 Oct 2024 18:58:54 +0900 Subject: [PATCH 11/18] =?UTF-8?q?refactor:=20=EC=A4=91=EC=B2=A9=EB=90=9C?= =?UTF-8?q?=20=EC=BD=94=EB=A3=A8=ED=8B=B4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/login/LoginViewModel.kt | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt index 143639c2..0ff81393 100644 --- a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt +++ b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt @@ -17,22 +17,20 @@ class LoginViewModel @Inject constructor( private val _eventFlow = MutableSharedFlow() val eventFlow = _eventFlow.asSharedFlow() - fun login(loginData: LoginData){ + suspend fun login(loginData: LoginData){ when(loginData){ is LoginData.Success -> { - viewModelScope.launch { - try { - val result = loginUseCase.requestGoogleLogin(email = loginData.email) - _eventFlow.emit( - result?.let { - LoginEvent.Success(it.isProfileSet) - } ?: run { - LoginEvent.Error - } - ) - }catch (e: Exception){ - e.printStackTrace() - } + try { + val result = loginUseCase.requestGoogleLogin(email = loginData.email) + _eventFlow.emit( + result?.let { + LoginEvent.Success(it.isProfileSet) + } ?: run { + LoginEvent.Error + } + ) + }catch (e: Exception){ + e.printStackTrace() } } is LoginData.Failed -> { From 118c9a5bc678846edcd3c4dd909e1812965a3cb9 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Wed, 16 Oct 2024 12:39:06 +0900 Subject: [PATCH 12/18] =?UTF-8?q?feat:=20MainViewModel=20-=20routeToNaviga?= =?UTF-8?q?te=20debounce=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/goalpanzi/mission_mate/core/main/MainScreen.kt | 7 +++++-- .../com/goalpanzi/mission_mate/core/main/MainViewModel.kt | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt b/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt index 50fe94ee..361d3bae 100644 --- a/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt +++ b/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt @@ -8,6 +8,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.goalpanzi.mission_mate.core.main.component.MainNavHost import com.goalpanzi.mission_mate.core.main.component.MainNavigator import com.goalpanzi.mission_mate.core.main.component.rememberMainNavigator +import kotlinx.coroutines.flow.collectLatest @Composable internal fun MainScreen( @@ -16,8 +17,10 @@ internal fun MainScreen( viewModel: MainViewModel = hiltViewModel() ) { LaunchedEffect(Unit) { - viewModel.navigationEvent.collect { - navigator.navController.navigate(it) + viewModel.navigationEvent.collectLatest { + if(navigator.navController.currentDestination?.route != it){ + navigator.navController.navigate(it) + } } } diff --git a/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainViewModel.kt b/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainViewModel.kt index 6b1d7898..b4b855a4 100644 --- a/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainViewModel.kt +++ b/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainViewModel.kt @@ -4,11 +4,14 @@ import androidx.lifecycle.ViewModel import com.goalpanzi.mission_mate.core.navigation.NavigationEventHandler import com.goalpanzi.mission_mate.core.navigation.di.AuthNavigation import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.debounce import javax.inject.Inject @HiltViewModel class MainViewModel @Inject constructor( @AuthNavigation navigationEventHandler : NavigationEventHandler ) : ViewModel() { - val navigationEvent = navigationEventHandler.routeToNavigate + @OptIn(FlowPreview::class) + val navigationEvent = navigationEventHandler.routeToNavigate.debounce(1_000) } From 1a6963d349a65841559bacaad311f38010e32280 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Wed, 16 Oct 2024 12:47:40 +0900 Subject: [PATCH 13/18] =?UTF-8?q?feat:=20Login=20Navigate=20=EB=B6=84?= =?UTF-8?q?=EA=B8=B0=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/goalpanzi/mission_mate/core/main/MainScreen.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt b/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt index 361d3bae..c4d517df 100644 --- a/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt +++ b/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt @@ -17,9 +17,13 @@ internal fun MainScreen( viewModel: MainViewModel = hiltViewModel() ) { LaunchedEffect(Unit) { - viewModel.navigationEvent.collectLatest { - if(navigator.navController.currentDestination?.route != it){ - navigator.navController.navigate(it) + viewModel.navigationEvent.collectLatest { route -> + if(navigator.navController.currentDestination?.route != route){ + if(route == "RouteModel.Login"){ + navigator.navigateToLogin() + }else { + navigator.navController.navigate(route) + } } } } From 4510205b9645e756852084ed792f755c95226f7f Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Wed, 16 Oct 2024 20:42:07 +0900 Subject: [PATCH 14/18] =?UTF-8?q?feat:=20debounce=20=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/goalpanzi/mission_mate/core/main/MainScreen.kt | 5 ++++- .../com/goalpanzi/mission_mate/core/main/MainViewModel.kt | 5 +---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt b/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt index c4d517df..040437c8 100644 --- a/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt +++ b/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainScreen.kt @@ -8,8 +8,11 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.goalpanzi.mission_mate.core.main.component.MainNavHost import com.goalpanzi.mission_mate.core.main.component.MainNavigator import com.goalpanzi.mission_mate.core.main.component.rememberMainNavigator +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.debounce +@OptIn(FlowPreview::class) @Composable internal fun MainScreen( startDestination: String, @@ -17,7 +20,7 @@ internal fun MainScreen( viewModel: MainViewModel = hiltViewModel() ) { LaunchedEffect(Unit) { - viewModel.navigationEvent.collectLatest { route -> + viewModel.navigationEvent.debounce(200).collectLatest { route -> if(navigator.navController.currentDestination?.route != route){ if(route == "RouteModel.Login"){ navigator.navigateToLogin() diff --git a/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainViewModel.kt b/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainViewModel.kt index b4b855a4..6b1d7898 100644 --- a/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainViewModel.kt +++ b/feature/main/src/main/java/com/goalpanzi/mission_mate/core/main/MainViewModel.kt @@ -4,14 +4,11 @@ import androidx.lifecycle.ViewModel import com.goalpanzi.mission_mate.core.navigation.NavigationEventHandler import com.goalpanzi.mission_mate.core.navigation.di.AuthNavigation import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.flow.debounce import javax.inject.Inject @HiltViewModel class MainViewModel @Inject constructor( @AuthNavigation navigationEventHandler : NavigationEventHandler ) : ViewModel() { - @OptIn(FlowPreview::class) - val navigationEvent = navigationEventHandler.routeToNavigate.debounce(1_000) + val navigationEvent = navigationEventHandler.routeToNavigate } From 9d46b7f502af06ba259872f08fd057029bd99c04 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Sun, 27 Oct 2024 19:00:13 +0900 Subject: [PATCH 15/18] =?UTF-8?q?fix:=20=EB=AF=B8=EC=85=98=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=ED=95=84=ED=84=B0=EA=B0=92=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 2 +- .../core/data/onboarding/mapper/OnboardingMapper.kt | 3 ++- .../mission_mate/core/domain/onboarding/model/Mission.kt | 3 ++- .../core/network/model/response/MissionResponse.kt | 3 ++- .../mission_mate/core/network/service/OnboardingService.kt | 4 ++-- .../feature/onboarding/screen/OnboardingViewModel.kt | 5 +++-- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 14a50714..323940df 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -43,7 +43,7 @@ android { getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) - isDebuggable = false + isDebuggable = true } } buildFeatures { diff --git a/core/data/onboarding/src/main/java/com/goalpanzi/mission_mate/core/data/onboarding/mapper/OnboardingMapper.kt b/core/data/onboarding/src/main/java/com/goalpanzi/mission_mate/core/data/onboarding/mapper/OnboardingMapper.kt index 82d75470..368afc65 100644 --- a/core/data/onboarding/src/main/java/com/goalpanzi/mission_mate/core/data/onboarding/mapper/OnboardingMapper.kt +++ b/core/data/onboarding/src/main/java/com/goalpanzi/mission_mate/core/data/onboarding/mapper/OnboardingMapper.kt @@ -22,7 +22,8 @@ fun CreateMissionBody.toRequest() : CreateMissionRequest { fun MissionResponse.toModel() : Mission { return Mission( missionId = missionId, - description = description + description = description, + missionStatus = missionStatus ) } diff --git a/core/domain/onboarding/src/main/java/com/goalpanzi/mission_mate/core/domain/onboarding/model/Mission.kt b/core/domain/onboarding/src/main/java/com/goalpanzi/mission_mate/core/domain/onboarding/model/Mission.kt index c24515b4..d38bd377 100644 --- a/core/domain/onboarding/src/main/java/com/goalpanzi/mission_mate/core/domain/onboarding/model/Mission.kt +++ b/core/domain/onboarding/src/main/java/com/goalpanzi/mission_mate/core/domain/onboarding/model/Mission.kt @@ -2,5 +2,6 @@ package com.goalpanzi.mission_mate.core.domain.onboarding.model data class Mission( val missionId : Long, - val description : String + val description : String, + val missionStatus : String ) diff --git a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/model/response/MissionResponse.kt b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/model/response/MissionResponse.kt index 11d2a981..6da3156c 100644 --- a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/model/response/MissionResponse.kt +++ b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/model/response/MissionResponse.kt @@ -5,5 +5,6 @@ import kotlinx.serialization.Serializable @Serializable data class MissionResponse( val missionId : Long, - val description : String + val description : String, + val missionStatus : String ) diff --git a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/service/OnboardingService.kt b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/service/OnboardingService.kt index 5ca72b42..4540949a 100644 --- a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/service/OnboardingService.kt +++ b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/service/OnboardingService.kt @@ -28,6 +28,6 @@ interface OnboardingService { @GET("/api/mission-members/me") suspend fun getJoinedMissions( - @Query("filter") filter : String = "PENDING,ONGOING" + @Query("filter") filter : String = "" ) : Response -} \ No newline at end of file +} diff --git a/feature/onboarding/src/main/java/com/goalpanzi/mission_mate/feature/onboarding/screen/OnboardingViewModel.kt b/feature/onboarding/src/main/java/com/goalpanzi/mission_mate/feature/onboarding/screen/OnboardingViewModel.kt index 6446dfaa..7c1db6a5 100644 --- a/feature/onboarding/src/main/java/com/goalpanzi/mission_mate/feature/onboarding/screen/OnboardingViewModel.kt +++ b/feature/onboarding/src/main/java/com/goalpanzi/mission_mate/feature/onboarding/screen/OnboardingViewModel.kt @@ -65,9 +65,10 @@ class OnboardingViewModel @Inject constructor( when (result) { is DomainResult.Success -> { result.data.missions.let { missions -> - if (missions.isNotEmpty() && isJoined != false) { + val missionInProgress = missions.lastOrNull { it.missionStatus == "IN_PROGRESS" } + if (missionInProgress != null) { _onboardingResultEvent.emit( - OnboardingResultEvent.SuccessWithJoinedMissions(missions.first()) + OnboardingResultEvent.SuccessWithJoinedMissions(missionInProgress) ) } else { _onboardingUiModel.emit( From 8e776495482ed1d6c4c91819cd1c84d120d4e222 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Sun, 27 Oct 2024 19:01:08 +0900 Subject: [PATCH 16/18] chore: isDebuggable false --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 323940df..14a50714 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -43,7 +43,7 @@ android { getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) - isDebuggable = true + isDebuggable = false } } buildFeatures { From 6391095f1e9e32e15e3c1d39ba99ed923af83dd5 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Mon, 28 Oct 2024 21:07:30 +0900 Subject: [PATCH 17/18] =?UTF-8?q?feat:=20missionStatus=20CREATED=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/onboarding/screen/OnboardingViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/onboarding/src/main/java/com/goalpanzi/mission_mate/feature/onboarding/screen/OnboardingViewModel.kt b/feature/onboarding/src/main/java/com/goalpanzi/mission_mate/feature/onboarding/screen/OnboardingViewModel.kt index 7c1db6a5..c39a28f8 100644 --- a/feature/onboarding/src/main/java/com/goalpanzi/mission_mate/feature/onboarding/screen/OnboardingViewModel.kt +++ b/feature/onboarding/src/main/java/com/goalpanzi/mission_mate/feature/onboarding/screen/OnboardingViewModel.kt @@ -65,7 +65,7 @@ class OnboardingViewModel @Inject constructor( when (result) { is DomainResult.Success -> { result.data.missions.let { missions -> - val missionInProgress = missions.lastOrNull { it.missionStatus == "IN_PROGRESS" } + val missionInProgress = missions.lastOrNull { it.missionStatus in setOf("IN_PROGRESS", "CREATED") } if (missionInProgress != null) { _onboardingResultEvent.emit( OnboardingResultEvent.SuccessWithJoinedMissions(missionInProgress) From 00cc90b22f7c80cd8e6961f5092a478efe06a61f Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Mon, 28 Oct 2024 21:23:34 +0900 Subject: [PATCH 18/18] release: 1.0.2 --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 14a50714..463c8a04 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -16,8 +16,8 @@ android { applicationId = "com.goalpanzi.mission_mate" minSdk = 26 targetSdk = 34 - versionCode = 4 - versionName = "1.0.1" + versionCode = 5 + versionName = "1.0.2" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables {