Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8bbb1f7
[ADD/#252] Mavericks 라이브러리 의존성 추가
MoonsuKang May 26, 2025
687a74f
[ADD/#252] Mavericks 초기화 추가
MoonsuKang May 26, 2025
0c45ebc
[ADD/#252] LifecycleOwner repeatOnStarted 확장 함수 추가
MoonsuKang May 26, 2025
3b9694c
[ADD/#252] BasePreview 컴포저블 구현
MoonsuKang May 26, 2025
26b2bf5
[REFACTOR/#252] Splash에 Mavericks Mvi 구조 적용
MoonsuKang May 26, 2025
82d9b96
[REFACTOR/#252] FCM 토큰 로직 core 패키지로 분리하여 FcmTokenProvider로 추출
MoonsuKang May 26, 2025
97859a3
[REFACTOR/#252] 소셜 로그인 로직 core:login 패키지로 분리 및 LoginSdk 추상화
MoonsuKang May 26, 2025
017caaa
[REFACTOR/#252] Login에 Mavericks Mvi 구조 및 soc 적용
MoonsuKang May 26, 2025
4ee4230
[REFACTOR/#252] SignUp(회원가입) 흐름 Route 단일화 및 MVI 기반 구조 개선
MoonsuKang May 26, 2025
68e9f1a
[REFACTOR/#252] SignUp 구조 변경에 따른 navigation parameter 수정
MoonsuKang May 26, 2025
0fdadc0
[REFACTOR/#252] NickNameTextField 구조 개선 및 상태 관리를 TextFieldValue → Str…
MoonsuKang May 26, 2025
0237d57
[REFACTOR/#252] SignUp 흐름 통합 및 parameter 수정
MoonsuKang May 26, 2025
2ffffb6
[ADD/#252] Mavericks ViewModel 바인딩을 위한 ViewModelsModule 추가
MoonsuKang May 26, 2025
003da52
[REFACTOR/#252] Scaffold를 Box로 변경하여 레이아웃 단순화
MoonsuKang May 26, 2025
f1625f2
[DEL/#252] 사용하지 않는 코드 제거
MoonsuKang May 26, 2025
876044c
[MOD/#252]: CLODYTheme -> ClodyTheme로 수정
MoonsuKang May 26, 2025
a61b83a
[REFACTOR/#252] 패키지 구조 변경
MoonsuKang May 26, 2025
f6c8271
trigger CI
MoonsuKang May 28, 2025
4753345
[FIX/#252] Ktlint formatting
MoonsuKang May 29, 2025
b9284e7
[REFACTOR/#252] allChecked 상태를 derived property로 변경하여 불필요한 상태 제거
MoonsuKang Jun 1, 2025
734def9
[REFACTOR/#252] ClearError Intent 처리 조건에 is 키워드 추가
MoonsuKang Jun 1, 2025
2dde0d1
[REFACTOR/#252] 앱 업데이트 흐름을 Intent 및 SideEffect 기반으로 리팩토링 및 ViewModel …
MoonsuKang Jun 1, 2025
72489f8
[REFACTOR/#252] allChecked가 derived property가 됨에 따른 코드 수정
MoonsuKang Jun 1, 2025
ceadb1c
[REFACTOR/#252] handleIntent 함수 분리 진행
MoonsuKang Jun 1, 2025
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
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ dependencies {
implementation(libs.accompanist.systemuicontroller)
implementation(libs.accompanist.insets)

// Mavericks
implementation(libs.bundles.mavericks)

// ETC
implementation(libs.timber)
implementation(libs.lottie.compose)
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/com/sopt/clody/ClodyApplication.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.sopt.clody

import android.app.Application
import com.airbnb.mvrx.Mavericks
import com.google.firebase.FirebaseApp
import com.kakao.sdk.common.KakaoSdk
import com.sopt.clody.presentation.utils.amplitude.AmplitudeUtils.initAmplitude
Expand All @@ -14,6 +15,7 @@ class ClodyApplication : Application() {
Timber.plant(Timber.DebugTree())
initKakaoSdk()
FirebaseApp.initializeApp(this)
Mavericks.initialize(this)
initAmplitude(applicationContext)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.sopt.clody.core
package com.sopt.clody.core.ad

import android.app.Activity

Expand Down
18 changes: 18 additions & 0 deletions app/src/main/java/com/sopt/clody/core/fcm/FcmTokenProvider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.sopt.clody.core.fcm

import com.google.firebase.ktx.Firebase
import com.google.firebase.messaging.ktx.messaging
import kotlinx.coroutines.tasks.await
import timber.log.Timber
import javax.inject.Inject

class FcmTokenProvider @Inject constructor() {
suspend fun getToken(): String? {
return try {
Firebase.messaging.token.await()
} catch (e: Exception) {
Timber.e("FCM 토큰 수신 실패: ${e.message}")
null
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.sopt.clody.core.login

@JvmInline
value class KakaoAccessToken(override val value: String) : LoginAccessToken
85 changes: 85 additions & 0 deletions app/src/main/java/com/sopt/clody/core/login/KakaoLoginSdk.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.sopt.clody.core.login

import android.content.Context
import com.kakao.sdk.auth.model.OAuthToken
import com.kakao.sdk.common.model.AuthError
import com.kakao.sdk.common.model.ClientError
import com.kakao.sdk.common.model.ClientErrorCause
import com.kakao.sdk.user.UserApiClient
import kotlinx.coroutines.suspendCancellableCoroutine
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

@Singleton
class KakaoLoginSdk @Inject constructor() : LoginSdk {
override suspend fun login(context: Context): Result<LoginAccessToken> = runCatching {
suspendCancellableCoroutine { continuation ->
val callback: (OAuthToken?, Throwable?) -> Unit = callback@{ token, throwable ->
if (!continuation.isActive) {
return@callback
}

when {
throwable != null -> {
if (throwable is ClientError && throwable.reason == ClientErrorCause.Cancelled) {
continuation.resumeWithException(LoginException.CancelException(throwable.message))
return@callback
}

continuation.resumeWithException(LoginException.AuthException(throwable.message))
}

token != null -> continuation.resume(KakaoAccessToken(token.accessToken))
}
}

val userApiClient = UserApiClient.instance

if (userApiClient.isKakaoTalkLoginAvailable(context)) {
userApiClient.loginWithKakaoTalk(
context,
callback = callback@{ oAuthToken, throwable ->
// 카카오톡이 설치되어 있으나 로그인되어 있지 않은 경우 대응
if (throwable is AuthError && throwable.statusCode == 302) {
userApiClient.loginWithKakaoAccount(context, callback = callback)
return@callback
}

callback(oAuthToken, throwable)
},
)
} else {
// 카카오톡 웹 로그인
userApiClient.loginWithKakaoAccount(context, callback = callback)
}
}
}

override suspend fun logout(): Result<Unit> = runCatching {
suspendCancellableCoroutine { continuation ->
val userApiClient = UserApiClient.instance

userApiClient.logout { throwable ->
when {
throwable != null -> continuation.resumeWithException(LoginException.AuthException(throwable.message))
else -> continuation.resume(Unit)
}
}
}
}

override suspend fun unlink(): Result<Unit> = runCatching {
suspendCancellableCoroutine { continuation ->
val userApiClient = UserApiClient.instance

userApiClient.unlink { throwable ->
when {
throwable != null -> continuation.resumeWithException(LoginException.AuthException(throwable.message))
else -> continuation.resume(Unit)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.sopt.clody.core.login

interface LoginAccessToken {
val value: String
}
6 changes: 6 additions & 0 deletions app/src/main/java/com/sopt/clody/core/login/LoginException.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.sopt.clody.core.login

sealed class LoginException(override val message: String?) : Exception(message) {
class CancelException(message: String?) : LoginException(message)
class AuthException(message: String?) : LoginException(message)
}
16 changes: 16 additions & 0 deletions app/src/main/java/com/sopt/clody/core/login/LoginModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.sopt.clody.core.login

import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent

@Module
@InstallIn(SingletonComponent::class)
abstract class LoginModule {

@Binds
abstract fun bindLoginSdk(
kakaoLoginSdk: KakaoLoginSdk,
): LoginSdk
}
9 changes: 9 additions & 0 deletions app/src/main/java/com/sopt/clody/core/login/LoginSdk.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.sopt.clody.core.login

import android.content.Context

interface LoginSdk {
suspend fun login(context: Context): Result<LoginAccessToken>
suspend fun logout(): Result<Unit>
suspend fun unlink(): Result<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.sopt.clody.data.ad
import android.app.Activity
import com.google.android.gms.ads.AdError
import com.google.android.gms.ads.FullScreenContentCallback
import com.sopt.clody.core.RewardAdShower
import com.sopt.clody.core.ad.RewardAdShower
import com.sopt.clody.data.remote.datasource.AdRemoteDataSource
import javax.inject.Inject

Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/sopt/clody/di/AdModule.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.sopt.clody.di

import com.sopt.clody.core.RewardAdShower
import com.sopt.clody.core.ad.RewardAdShower
import com.sopt.clody.data.ad.RewardAdShowerImpl
import dagger.Binds
import dagger.Module
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.sopt.clody.presentation.di

import com.airbnb.mvrx.hilt.AssistedViewModelFactory
import com.airbnb.mvrx.hilt.MavericksViewModelComponent
import com.airbnb.mvrx.hilt.ViewModelKey
import com.sopt.clody.presentation.ui.auth.signup.SignUpViewModel
import com.sopt.clody.presentation.ui.login.LoginViewModel
import com.sopt.clody.presentation.ui.splash.SplashViewModel
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.multibindings.IntoMap

@Module
@InstallIn(MavericksViewModelComponent::class)
interface ViewModelsModule {

@Binds
@IntoMap
@ViewModelKey(SplashViewModel::class)
fun bindSplashViewModelFactory(
factory: SplashViewModel.Factory,
): AssistedViewModelFactory<*, *>

@Binds
@IntoMap
@ViewModelKey(LoginViewModel::class)
fun bindLoginViewModelFactory(
factory: LoginViewModel.Factory,
): AssistedViewModelFactory<*, *>

@Binds
@IntoMap
@ViewModelKey(SignUpViewModel::class)
fun bindSignUpViewModelFactory(
factory: SignUpViewModel.Factory,
): AssistedViewModelFactory<*, *>
}
Loading