From a01d87bc1bf1844a2da2524a9f3c12b6102f149a Mon Sep 17 00:00:00 2001 From: chanu Date: Thu, 17 Jul 2025 21:24:36 +0900 Subject: [PATCH 1/9] #179 [move] : move directory --- .../com/teamwable/data/repositoryimpl/DefaultAuthRepository.kt | 2 +- .../data/repositoryimpl/DefaultCommunityRepository.kt | 2 +- .../data/repositoryimpl/DefaultFeedImageRepository.kt | 2 +- .../teamwable/data/repositoryimpl/DefaultProfileRepository.kt | 2 +- .../java/com/teamwable/network}/util/runSuspendCatching.kt | 3 +-- 5 files changed, 5 insertions(+), 6 deletions(-) rename core/{data/src/main/java/com/teamwable/data => network/src/main/java/com/teamwable/network}/util/runSuspendCatching.kt (89%) diff --git a/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultAuthRepository.kt b/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultAuthRepository.kt index e2995e75..7c4258b1 100644 --- a/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultAuthRepository.kt +++ b/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultAuthRepository.kt @@ -2,10 +2,10 @@ package com.teamwable.data.repositoryimpl import com.teamwable.data.mapper.toModel.toUserModel import com.teamwable.data.repository.AuthRepository -import com.teamwable.data.util.runHandledCatching import com.teamwable.model.auth.UserModel import com.teamwable.network.datasource.AuthService import com.teamwable.network.dto.request.RequestSocialLoginDto +import com.teamwable.network.util.runHandledCatching import javax.inject.Inject internal class DefaultAuthRepository @Inject constructor( diff --git a/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultCommunityRepository.kt b/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultCommunityRepository.kt index 6a8af054..ae4b37af 100644 --- a/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultCommunityRepository.kt +++ b/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultCommunityRepository.kt @@ -3,9 +3,9 @@ package com.teamwable.data.repositoryimpl import com.teamwable.data.mapper.toModel.toCommunityModel import com.teamwable.data.mapper.toModel.toRequestCommunityDto import com.teamwable.data.repository.CommunityRepository -import com.teamwable.data.util.runHandledCatching import com.teamwable.model.community.CommunityModel import com.teamwable.network.datasource.CommunityService +import com.teamwable.network.util.runHandledCatching import com.teamwable.network.util.toCustomError import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch diff --git a/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultFeedImageRepository.kt b/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultFeedImageRepository.kt index 1cf595f7..2114cd11 100644 --- a/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultFeedImageRepository.kt +++ b/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultFeedImageRepository.kt @@ -4,7 +4,7 @@ import com.teamwable.data.gallery.BitmapFetcher import com.teamwable.data.gallery.GallerySaver import com.teamwable.data.gallery.GallerySaver.Companion.FILE_EXTENSION import com.teamwable.data.repository.FeedImageRepository -import com.teamwable.data.util.runHandledCatching +import com.teamwable.network.util.runHandledCatching import javax.inject.Inject internal class DefaultFeedImageRepository @Inject constructor( diff --git a/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultProfileRepository.kt b/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultProfileRepository.kt index 3f1979f6..06c79d11 100644 --- a/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultProfileRepository.kt +++ b/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultProfileRepository.kt @@ -7,13 +7,13 @@ import com.teamwable.data.mapper.toModel.toMemberDataModel import com.teamwable.data.mapper.toModel.toProfile import com.teamwable.data.repository.ProfileRepository import com.teamwable.data.util.createImagePart -import com.teamwable.data.util.runHandledCatching import com.teamwable.model.Profile import com.teamwable.model.profile.MemberDataModel import com.teamwable.model.profile.MemberInfoEditModel import com.teamwable.network.datasource.ProfileService import com.teamwable.network.dto.request.RequestWithdrawalDto import com.teamwable.network.util.handleThrowable +import com.teamwable.network.util.runHandledCatching import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody diff --git a/core/data/src/main/java/com/teamwable/data/util/runSuspendCatching.kt b/core/network/src/main/java/com/teamwable/network/util/runSuspendCatching.kt similarity index 89% rename from core/data/src/main/java/com/teamwable/data/util/runSuspendCatching.kt rename to core/network/src/main/java/com/teamwable/network/util/runSuspendCatching.kt index 425bdf36..8d6713b5 100644 --- a/core/data/src/main/java/com/teamwable/data/util/runSuspendCatching.kt +++ b/core/network/src/main/java/com/teamwable/network/util/runSuspendCatching.kt @@ -1,6 +1,5 @@ -package com.teamwable.data.util +package com.teamwable.network.util -import com.teamwable.network.util.toCustomError import kotlinx.coroutines.TimeoutCancellationException import kotlin.coroutines.cancellation.CancellationException From d4c772db2eec4c86dbcfa17978f2f10e8dcdba25 Mon Sep 17 00:00:00 2001 From: chanu Date: Thu, 17 Jul 2025 21:48:02 +0900 Subject: [PATCH 2/9] #179 [remove] : remove ununsed annotation --- .../src/main/java/com/teamwable/network/di/NetworkModule.kt | 3 +-- .../src/main/java/com/teamwable/network/di/Qualifier.kt | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt b/core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt index 319d80c2..3a95f14c 100644 --- a/core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt +++ b/core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt @@ -35,7 +35,7 @@ internal object NetworkModule { @Singleton @Provides fun provideOkHttpClient( - @AccessToken tokenInterceptor: Interceptor, + tokenInterceptor: Interceptor, loggingInterceptor: HttpLoggingInterceptor, ): OkHttpClient = OkHttpClient.Builder() @@ -47,7 +47,6 @@ internal object NetworkModule { @Provides @Singleton - @AccessToken fun provideAuthInterceptor(interceptor: TokenInterceptor): Interceptor = interceptor @Singleton diff --git a/core/network/src/main/java/com/teamwable/network/di/Qualifier.kt b/core/network/src/main/java/com/teamwable/network/di/Qualifier.kt index 8003ce63..2962f106 100644 --- a/core/network/src/main/java/com/teamwable/network/di/Qualifier.kt +++ b/core/network/src/main/java/com/teamwable/network/di/Qualifier.kt @@ -6,10 +6,6 @@ import javax.inject.Qualifier @Retention(AnnotationRetention.BINARY) annotation class WableRetrofit -@Qualifier -@Retention(AnnotationRetention.BINARY) -annotation class AccessToken - @Qualifier @Retention(AnnotationRetention.BINARY) annotation class WithoutTokenInterceptor From 968e3dd4f5c1352cfd1a4533aff3209866b7c18c Mon Sep 17 00:00:00 2001 From: chanu Date: Thu, 17 Jul 2025 21:49:17 +0900 Subject: [PATCH 3/9] =?UTF-8?q?#179=20[feat]=20:=20authenticator=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../teamwable/network/TokenAuthenticator.kt | 85 +++++++++++++++ .../com/teamwable/network/TokenInterceptor.kt | 102 ++---------------- .../com/teamwable/network/di/NetworkModule.kt | 3 + 3 files changed, 94 insertions(+), 96 deletions(-) create mode 100644 core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt diff --git a/core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt b/core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt new file mode 100644 index 00000000..408063d8 --- /dev/null +++ b/core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt @@ -0,0 +1,85 @@ +package com.teamwable.network + +import android.content.Context +import android.content.Intent +import android.widget.Toast +import com.teamwable.datastore.datasource.WablePreferencesDataSource +import com.teamwable.network.datasource.AuthService +import com.teamwable.network.util.runSuspendCatching +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import okhttp3.Authenticator +import okhttp3.Request +import okhttp3.Response +import okhttp3.Route +import timber.log.Timber +import javax.inject.Inject + +class TokenAuthenticator @Inject constructor( + private val dataStore: WablePreferencesDataSource, + private val authService: AuthService, + @ApplicationContext private val context: Context, +) : Authenticator { + private val mutex = Mutex() + private var currentToast: Toast? = null + + override fun authenticate(route: Route?, response: Response): Request? { + if (response.code != 401) return null + + return runBlocking { + val newAccessToken = refreshToken() ?: return@runBlocking null + response.request + .newBuilder() + .header("Authorization", newAccessToken) + .build() + } + } + + private suspend fun refreshToken(): String? { + return mutex.withLock { + val accessToken = dataStore.accessToken.first() + val refreshToken = dataStore.refreshToken.first() + + runSuspendCatching { + authService.getReissueToken(accessToken, refreshToken) + }.onSuccess { + val newAccess = "Bearer ${it.data.accessToken}" + dataStore.updateAccessToken(newAccess) + dataStore.updateRefreshToken(it.data.refreshToken) + return newAccess + }.onFailure { + Timber.e(it) + dataStore.clear() + notifyReLoginRequired() + }.getOrNull()?.let { "Bearer ${it.data.accessToken}" } + } + } + + private fun notifyReLoginRequired() { + CoroutineScope(Dispatchers.Main).launch { + showToast("재 로그인이 필요해요") + restartApp() + } + } + + private fun showToast(message: String) { + currentToast?.cancel() + currentToast = Toast.makeText(context, message, Toast.LENGTH_SHORT) + currentToast?.show() + } + + private fun restartApp() { + val restartIntent = context.packageManager + .getLaunchIntentForPackage(context.packageName) + ?.component + ?.let(Intent::makeRestartActivityTask) + + context.startActivity(restartIntent) + } +} diff --git a/core/network/src/main/java/com/teamwable/network/TokenInterceptor.kt b/core/network/src/main/java/com/teamwable/network/TokenInterceptor.kt index 0b920ca7..6eb0e965 100644 --- a/core/network/src/main/java/com/teamwable/network/TokenInterceptor.kt +++ b/core/network/src/main/java/com/teamwable/network/TokenInterceptor.kt @@ -1,112 +1,22 @@ package com.teamwable.network -import android.app.Application -import android.content.Intent -import android.widget.Toast import com.teamwable.datastore.datasource.WablePreferencesDataSource -import com.teamwable.network.datasource.AuthService -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import kotlinx.coroutines.withContext import okhttp3.Interceptor import okhttp3.Request import okhttp3.Response import javax.inject.Inject -import javax.inject.Singleton -@Singleton class TokenInterceptor @Inject constructor( - private val wablePreferencesDataSource: WablePreferencesDataSource, - private val context: Application, - private val authService: AuthService, + private val dataStore: WablePreferencesDataSource, ) : Interceptor { - private val mutex = Mutex() - private var currentToast: Toast? = null - override fun intercept(chain: Interceptor.Chain): Response { - val originalRequest = chain.request() - var response = chain.proceed(originalRequest.newAuthBuilder()) - - if (response.code == CODE_TOKEN_EXPIRE) { - response.close() - val tokenRefreshed = runBlocking { - refreshTokenIfNeeded() - } - if (tokenRefreshed) { - response = chain.proceed(originalRequest.newAuthBuilder()) - } else { - handleFailedTokenReissue() - } - } - return response - } - - private suspend fun refreshTokenIfNeeded(): Boolean { - mutex.withLock { - val accessToken = wablePreferencesDataSource.accessToken.first() - val refreshToken = wablePreferencesDataSource.refreshToken.first() - - return try { - val tokenResult = runBlocking(Dispatchers.IO) { - authService.getReissueToken(accessToken, refreshToken) - } - - when (tokenResult.success) { - true -> { - wablePreferencesDataSource.updateAccessToken( - BEARER + tokenResult.data.accessToken, - ) - true - } - - false -> false - } - } catch (e: Exception) { - false - } - } - } - - private fun handleFailedTokenReissue() = CoroutineScope(Dispatchers.Main).launch { - showToast() - withContext(Dispatchers.IO) { - wablePreferencesDataSource.clear() - } - restartActivity() - } - - private fun showToast() { - currentToast?.cancel() - currentToast = Toast.makeText(context, "재 로그인이 필요해요", Toast.LENGTH_SHORT) - currentToast?.show() - } - - private suspend fun restartActivity() = with(context) { - mutex.withLock { - startActivity( - Intent.makeRestartActivityTask( - packageManager.getLaunchIntentForPackage(packageName)?.component, - ), - ) - } - } - - private fun Request.newAuthBuilder() = newBuilder() - .addHeader( - name = AUTHORIZATION, - value = runBlocking { - wablePreferencesDataSource.accessToken.first() - }, - ).build() + val accessToken = runBlocking { dataStore.accessToken.first() } + val authRequest: Request = chain.request().newBuilder() + .addHeader("Authorization", accessToken) + .build() - companion object { - const val CODE_TOKEN_EXPIRE = 401 - const val AUTHORIZATION = "Authorization" - const val BEARER = "Bearer " + return chain.proceed(authRequest) } } diff --git a/core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt b/core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt index 3a95f14c..3ec3dc28 100644 --- a/core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt +++ b/core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt @@ -2,6 +2,7 @@ package com.teamwable.network.di import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import com.teamwable.network.BuildConfig +import com.teamwable.network.TokenAuthenticator import com.teamwable.network.TokenInterceptor import com.teamwable.network.util.isJsonArray import com.teamwable.network.util.isJsonObject @@ -37,12 +38,14 @@ internal object NetworkModule { fun provideOkHttpClient( tokenInterceptor: Interceptor, loggingInterceptor: HttpLoggingInterceptor, + tokenAuthenticator: TokenAuthenticator, ): OkHttpClient = OkHttpClient.Builder() .connectTimeout(5, TimeUnit.SECONDS) .readTimeout(5, TimeUnit.SECONDS) .addInterceptor(tokenInterceptor) .addInterceptor(loggingInterceptor) + .authenticator(tokenAuthenticator) .build() @Provides From d5117e575b81cfb172d76cbd71586c34034f3fa0 Mon Sep 17 00:00:00 2001 From: chanu Date: Fri, 18 Jul 2025 01:16:36 +0900 Subject: [PATCH 4/9] =?UTF-8?q?#179=20[feat]=20:=20App=20=EC=9E=AC?= =?UTF-8?q?=EC=8B=9C=EC=9E=91=20=EB=B0=8F=20=ED=86=A0=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=EC=9D=84=20=EC=9C=84=ED=95=9C=20AppReStarter?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20DI=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/restarter/AppReStarter.kt | 7 ++++ .../common/restarter/AppReStarterModule.kt | 17 +++++++++ .../common/restarter/DefaultAppReStarter.kt | 36 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 core/common/src/main/java/com/teamwable/common/restarter/AppReStarter.kt create mode 100644 core/common/src/main/java/com/teamwable/common/restarter/AppReStarterModule.kt create mode 100644 core/common/src/main/java/com/teamwable/common/restarter/DefaultAppReStarter.kt diff --git a/core/common/src/main/java/com/teamwable/common/restarter/AppReStarter.kt b/core/common/src/main/java/com/teamwable/common/restarter/AppReStarter.kt new file mode 100644 index 00000000..1fe2f01e --- /dev/null +++ b/core/common/src/main/java/com/teamwable/common/restarter/AppReStarter.kt @@ -0,0 +1,7 @@ +package com.teamwable.common.restarter + +interface AppReStarter { + fun restartApp() + + fun makeToast(message: String) +} diff --git a/core/common/src/main/java/com/teamwable/common/restarter/AppReStarterModule.kt b/core/common/src/main/java/com/teamwable/common/restarter/AppReStarterModule.kt new file mode 100644 index 00000000..a451a6a6 --- /dev/null +++ b/core/common/src/main/java/com/teamwable/common/restarter/AppReStarterModule.kt @@ -0,0 +1,17 @@ +package com.teamwable.common.restarter + +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +abstract class AppReStarterModule { + @Binds + @Singleton + abstract fun bindsAppRestarter( + appRestarter: DefaultAppReStarter, + ): AppReStarter +} diff --git a/core/common/src/main/java/com/teamwable/common/restarter/DefaultAppReStarter.kt b/core/common/src/main/java/com/teamwable/common/restarter/DefaultAppReStarter.kt new file mode 100644 index 00000000..d6235862 --- /dev/null +++ b/core/common/src/main/java/com/teamwable/common/restarter/DefaultAppReStarter.kt @@ -0,0 +1,36 @@ +package com.teamwable.common.restarter + +import android.content.Context +import android.content.Intent +import android.widget.Toast +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import javax.inject.Inject + +class DefaultAppReStarter @Inject constructor( + @ApplicationContext private val context: Context, +) : AppReStarter { + private var currentToast: Toast? = null + private val scope = CoroutineScope(Dispatchers.Main) + + override fun restartApp() { + scope.launch { + val restartIntent = context.packageManager + .getLaunchIntentForPackage(context.packageName) + ?.component + ?.let(Intent::makeRestartActivityTask) + + context.startActivity(restartIntent) + } + } + + override fun makeToast(message: String) { + scope.launch { + currentToast?.cancel() + currentToast = Toast.makeText(context, message, Toast.LENGTH_SHORT) + currentToast?.show() + } + } +} From b818d23fb98c057ba877cc57760041383984fab1 Mon Sep 17 00:00:00 2001 From: chanu Date: Fri, 18 Jul 2025 01:16:55 +0900 Subject: [PATCH 5/9] #179 [add] : add common module dependency in network module --- core/network/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index e6cf361e..9fdf4657 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -32,5 +32,6 @@ android { dependencies { implementation(project(":core:model")) implementation(project(":core:datastore")) + implementation(project(":core:common")) implementation(libs.paging) } From 5e024dd99c9257f8b5663cc7f781ac1e21d0c493 Mon Sep 17 00:00:00 2001 From: chanu Date: Fri, 18 Jul 2025 01:20:07 +0900 Subject: [PATCH 6/9] #179 [mod] : use app restarter interface in authenticator, fragment --- .../teamwable/network/TokenAuthenticator.kt | 32 +++---------------- .../hamburger/ProfileDeleteConfirmFragment.kt | 12 +++---- .../hamburger/ProfileHamburgerBottomSheet.kt | 13 ++++---- 3 files changed, 16 insertions(+), 41 deletions(-) diff --git a/core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt b/core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt index 408063d8..e0e35ac2 100644 --- a/core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt +++ b/core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt @@ -1,16 +1,10 @@ package com.teamwable.network -import android.content.Context -import android.content.Intent -import android.widget.Toast +import com.teamwable.common.restarter.AppReStarter import com.teamwable.datastore.datasource.WablePreferencesDataSource import com.teamwable.network.datasource.AuthService import com.teamwable.network.util.runSuspendCatching -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -24,10 +18,9 @@ import javax.inject.Inject class TokenAuthenticator @Inject constructor( private val dataStore: WablePreferencesDataSource, private val authService: AuthService, - @ApplicationContext private val context: Context, + private val appRestarter: AppReStarter, ) : Authenticator { private val mutex = Mutex() - private var currentToast: Toast? = null override fun authenticate(route: Route?, response: Response): Request? { if (response.code != 401) return null @@ -62,24 +55,7 @@ class TokenAuthenticator @Inject constructor( } private fun notifyReLoginRequired() { - CoroutineScope(Dispatchers.Main).launch { - showToast("재 로그인이 필요해요") - restartApp() - } - } - - private fun showToast(message: String) { - currentToast?.cancel() - currentToast = Toast.makeText(context, message, Toast.LENGTH_SHORT) - currentToast?.show() - } - - private fun restartApp() { - val restartIntent = context.packageManager - .getLaunchIntentForPackage(context.packageName) - ?.component - ?.let(Intent::makeRestartActivityTask) - - context.startActivity(restartIntent) + appRestarter.makeToast("재 로그인이 필요해요") + appRestarter.restartApp() } } diff --git a/feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileDeleteConfirmFragment.kt b/feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileDeleteConfirmFragment.kt index e9f0d0b4..01103dbe 100644 --- a/feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileDeleteConfirmFragment.kt +++ b/feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileDeleteConfirmFragment.kt @@ -1,10 +1,10 @@ package com.teamwable.profile.hamburger -import android.content.Intent import androidx.fragment.app.viewModels import androidx.lifecycle.flowWithLifecycle import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs +import com.teamwable.common.restarter.AppReStarter import com.teamwable.common.uistate.UiState import com.teamwable.common.util.AmplitudeAuthTag.CLICK_NEXT_DELETEGUIDE import com.teamwable.common.util.AmplitudeUtil.trackEvent @@ -23,6 +23,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import timber.log.Timber +import javax.inject.Inject @AndroidEntryPoint class ProfileDeleteConfirmFragment : BindingFragment(FragmentProfileDeleteConfirmBinding::inflate) { @@ -31,6 +32,9 @@ class ProfileDeleteConfirmFragment : BindingFragment() + @Inject + lateinit var appRestarter: AppReStarter + override fun initView() { reasons = args.reasons.toList() @@ -62,11 +66,7 @@ class ProfileDeleteConfirmFragment : BindingFragment(BottomsheetProfileHamburgerBinding::inflate) { private val viewModel: ProfileHamburgerViewModel by viewModels() + @Inject + lateinit var appRestarter: AppReStarter + override fun initView() { initAccountInformationBtnClickListener() initNotificationSettingBtnClickListener() @@ -69,10 +72,6 @@ class ProfileHamburgerBottomSheet : BindingBottomSheetFragment Date: Fri, 18 Jul 2025 01:23:45 +0900 Subject: [PATCH 7/9] =?UTF-8?q?#179=20[mod]=20:=20=EB=AC=B4=ED=95=9C=20401?= =?UTF-8?q?=20=EB=A3=A8=ED=94=84=20=EB=B0=A9=EC=A7=80=EC=9A=A9=20custom=20?= =?UTF-8?q?header=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/teamwable/network/TokenAuthenticator.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt b/core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt index e0e35ac2..361805c0 100644 --- a/core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt +++ b/core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt @@ -25,11 +25,14 @@ class TokenAuthenticator @Inject constructor( override fun authenticate(route: Route?, response: Response): Request? { if (response.code != 401) return null + if (response.request.header("Authorization-Retry") != null) return null + return runBlocking { val newAccessToken = refreshToken() ?: return@runBlocking null response.request .newBuilder() .header("Authorization", newAccessToken) + .header("Authorization-Retry", "true") .build() } } @@ -50,7 +53,8 @@ class TokenAuthenticator @Inject constructor( Timber.e(it) dataStore.clear() notifyReLoginRequired() - }.getOrNull()?.let { "Bearer ${it.data.accessToken}" } + } + null } } From 683fb272e7dfcd58b43efb10dc20610bc3f4a11f Mon Sep 17 00:00:00 2001 From: chanu Date: Fri, 18 Jul 2025 01:48:54 +0900 Subject: [PATCH 8/9] #179 [chore] : remove unncessary scope --- .../profile/hamburger/ProfileHamburgerBottomSheet.kt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileHamburgerBottomSheet.kt b/feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileHamburgerBottomSheet.kt index 74cb6ae6..680dcf42 100644 --- a/feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileHamburgerBottomSheet.kt +++ b/feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileHamburgerBottomSheet.kt @@ -8,12 +8,9 @@ import com.teamwable.profile.databinding.BottomsheetProfileHamburgerBinding import com.teamwable.ui.base.BindingBottomSheetFragment import com.teamwable.ui.component.TwoButtonDialog import com.teamwable.ui.extensions.openUri -import com.teamwable.ui.extensions.viewLifeCycleScope import com.teamwable.ui.type.DialogType import com.teamwable.ui.util.Arg.DIALOG_RESULT import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint @@ -64,10 +61,8 @@ class ProfileHamburgerBottomSheet : BindingBottomSheetFragment - viewLifeCycleScope.launch(Dispatchers.Main) { - viewModel.saveIsAutoLogin(false) - navigateToSplashScreen() - } + viewModel.saveIsAutoLogin(false) + navigateToSplashScreen() } } From 32861c34256e044a9bcf8bafff25488c44367b84 Mon Sep 17 00:00:00 2001 From: chanu Date: Fri, 18 Jul 2025 02:06:36 +0900 Subject: [PATCH 9/9] #179 [chore] : remove unnecessary di provides in tokenInterceptor --- .../main/java/com/teamwable/network/di/NetworkModule.kt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt b/core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt index 3ec3dc28..b40cfa9e 100644 --- a/core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt +++ b/core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt @@ -11,7 +11,6 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import kotlinx.serialization.json.Json -import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor @@ -36,7 +35,7 @@ internal object NetworkModule { @Singleton @Provides fun provideOkHttpClient( - tokenInterceptor: Interceptor, + tokenInterceptor: TokenInterceptor, loggingInterceptor: HttpLoggingInterceptor, tokenAuthenticator: TokenAuthenticator, ): OkHttpClient = @@ -48,10 +47,6 @@ internal object NetworkModule { .authenticator(tokenAuthenticator) .build() - @Provides - @Singleton - fun provideAuthInterceptor(interceptor: TokenInterceptor): Interceptor = interceptor - @Singleton @Provides fun provideLoggingInterceptor(): HttpLoggingInterceptor {