Skip to content
Draft
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
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ android {
getByName("debug") {
isDebuggable = true
applicationIdSuffix = ".dev"
buildConfigField("String", "GOOGLE_WEB_CLIENT_ID", getApiKey("DEBUG_GOOGLE_WEB_CLIENT_ID"))
manifestPlaceholders += mapOf(
"appName" to "@string/app_name_dev",
)
Expand All @@ -42,6 +43,7 @@ android {
isMinifyEnabled = true
isShrinkResources = true
signingConfig = signingConfigs.getByName("release")
buildConfigField("String", "GOOGLE_WEB_CLIENT_ID", getApiKey("RELEASE_GOOGLE_WEB_CLIENT_ID"))
manifestPlaceholders += mapOf(
"appName" to "@string/app_name",
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ enum class BookStatus(val value: String) {
}
}

companion object Companion {
companion object {
fun fromValue(value: String): BookStatus? {
return entries.find { it.value == value }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import com.ninecraft.booket.core.model.UserState
import kotlinx.coroutines.flow.Flow

interface AuthRepository {
suspend fun login(accessToken: String): Result<Unit>
suspend fun login(
providerType: String,
token: String,
): Result<Unit>

suspend fun logout(): Result<Unit>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,20 @@ import com.ninecraft.booket.core.di.DataScope
import dev.zacsweers.metro.SingleIn
import kotlinx.coroutines.flow.map

private const val KAKAO_PROVIDER_TYPE = "KAKAO"

@SingleIn(DataScope::class)
@Inject
class DefaultAuthRepository(
private val service: ReedService,
private val tokenDataSource: TokenDataSource,
) : AuthRepository {
override suspend fun login(accessToken: String) = runSuspendCatching {
override suspend fun login(
providerType: String,
token: String,
) = runSuspendCatching {
val response = service.login(
LoginRequest(
providerType = KAKAO_PROVIDER_TYPE,
oauthToken = accessToken,
providerType = providerType,
oauthToken = token,
),
)
saveTokens(response.accessToken, response.refreshToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class DefaultNotificationDataSource(
}
}

companion object Companion {
companion object {
private val USER_NOTIFICATION_ENABLED = booleanPreferencesKey("USER_NOTIFICATION_ENABLED")
private val LAST_SYNCED_NOTIFICATION_ENABLED = booleanPreferencesKey("LAST_SYNCED_NOTIFICATION_ENABLED")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class DefaultTokenDataSource(
}.orEmpty()
}

companion object Companion {
companion object {
private val ACCESS_TOKEN = stringPreferencesKey("ACCESS_TOKEN")
private val REFRESH_TOKEN = stringPreferencesKey("REFRESH_TOKEN")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ import com.ninecraft.booket.core.designsystem.theme.Kakao
import com.ninecraft.booket.core.designsystem.theme.ReedTheme

enum class ReedButtonColorStyle {
PRIMARY, SECONDARY, TERTIARY, STROKE, TEXT, KAKAO;
PRIMARY, SECONDARY, TERTIARY, STROKE, TEXT, KAKAO, GOOGLE;

@Composable
fun containerColor(isPressed: Boolean) = when (this) {
PRIMARY -> if (isPressed) ReedTheme.colors.bgPrimaryPressed else ReedTheme.colors.bgPrimary
SECONDARY -> if (isPressed) ReedTheme.colors.bgSecondaryPressed else ReedTheme.colors.bgSecondary
TERTIARY -> if (isPressed) ReedTheme.colors.bgTertiaryPressed else ReedTheme.colors.bgTertiary
STROKE -> if (isPressed) ReedTheme.colors.basePrimary else ReedTheme.colors.basePrimary
STROKE -> ReedTheme.colors.basePrimary
TEXT -> Color.Transparent
KAKAO -> Kakao
GOOGLE -> ReedTheme.colors.basePrimary
}

@Composable
Expand All @@ -26,8 +27,9 @@ enum class ReedButtonColorStyle {
SECONDARY -> ReedTheme.colors.contentPrimary
TERTIARY -> ReedTheme.colors.contentBrand
STROKE -> ReedTheme.colors.contentBrand
TEXT -> ReedTheme.colors.borderBrand
TEXT -> ReedTheme.colors.contentTertiary
KAKAO -> ReedTheme.colors.contentPrimary
GOOGLE -> ReedTheme.colors.contentPrimary
}

@Composable
Expand All @@ -42,6 +44,7 @@ enum class ReedButtonColorStyle {
@Composable
fun borderStroke() = when (this) {
STROKE -> BorderStroke(1.dp, ReedTheme.colors.borderBrand)
GOOGLE -> BorderStroke(1.dp, ReedTheme.colors.borderPrimary)
else -> null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ val Blue700 = Color(0xFF007BFF)
val Blue800 = Color(0xFF1269EC)
val Blue900 = Color(0xFF1F47CD)

val Kakao = Color(0xFFFBD300)
val Kakao = Color(0xFFFFEB00)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

카카오 색상이 공식 브랜드 가이드라인과 맞지 않음

업데이트된 색상 0xFFFFEB00이 공식 카카오 브랜드 색상과 일치하지 않습니다. 공식 카카오 브랜드 색상은 #FEE102이며, 카카오톡 로고 색상은 #ffe812입니다. 이 값들을 기반으로 올바른 색상값을 확인하고 적용해 주세요.

🤖 Prompt for AI Agents
In
core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Color.kt
around line 66, the Kakao color is set to 0xFFFFEB00 which does not match the
official brand values; update the value to the official Kakao brand hex
0xFFFEE102 (or use 0xFFFFE812 where the KakaoTalk logo color is required) by
replacing the current literal with the correct 0xFFFEE102 constant for the
primary Kakao color.

val Blank = Color(0xFFD6D6D6)
val HomeBg = Color(0xFFF0F9E8)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ enum class RecordSort(val value: String) {
}
}

companion object Companion {
companion object {
fun fromValue(value: String): RecordSort? {
return entries.find { it.value == value }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ enum class LibraryFilterOption(val value: String) {
}
}

companion object Companion {
companion object {
fun fromValue(value: String): LibraryFilterOption? {
return entries.find { it.value == value }
}
Expand Down
23 changes: 23 additions & 0 deletions feature/login/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
@file:Suppress("INLINE_FROM_HIGHER_PLATFORM")

import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties

plugins {
alias(libs.plugins.booket.android.feature)
alias(libs.plugins.kotlin.serialization)
Expand All @@ -8,11 +10,32 @@ plugins {

android {
namespace = "com.ninecraft.booket.feature.login"

buildTypes {
getByName("debug") {
buildConfigField("String", "GOOGLE_WEB_CLIENT_ID", getApiKey("DEBUG_GOOGLE_WEB_CLIENT_ID"))
}

getByName("release") {
buildConfigField("String", "GOOGLE_WEB_CLIENT_ID", getApiKey("RELEASE_GOOGLE_WEB_CLIENT_ID"))
}
}

buildFeatures {
buildConfig = true
}
}

dependencies {
implementations(
libs.logger,
libs.kakao.auth,
libs.androidx.credentials,
libs.androidx.credentials.play.services.auth,
libs.googleid,
)
}

fun getApiKey(propertyKey: String): String {
return gradleLocalProperties(rootDir, providers).getProperty(propertyKey)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,62 @@ package com.ninecraft.booket.feature.login
import android.widget.Toast
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import com.ninecraft.booket.feature.login.client.GoogleLoginClient
import com.ninecraft.booket.feature.login.client.KakaoLoginClient
import com.skydoves.compose.effects.RememberedEffect
import kotlinx.coroutines.launch

@Composable
internal fun HandleLoginSideEffects(
state: LoginUiState,
eventSink: (LoginUiEvent) -> Unit,
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val kakaoLoginClient = remember { KakaoLoginClient() }
val googleLoginClient = remember { GoogleLoginClient() }

RememberedEffect(state.sideEffect) {
when (state.sideEffect) {
is LoginSideEffect.KakaoLogin -> {
kakaoLoginClient.loginWithKakao(
context = context,
onSuccess = { token ->
eventSink(LoginUiEvent.Login(token))
eventSink(
LoginUiEvent.Login(
providerType = LoginUiEvent.PROVIDER_TYPE_KAKAO,
token = token,
),
)
},
onFailure = { errorMessage ->
eventSink(LoginUiEvent.LoginFailure(errorMessage))
},
)
}

is LoginSideEffect.GoogleLogin -> {
scope.launch {
googleLoginClient.loginWithGoogle(
context = context,
webClientId = BuildConfig.GOOGLE_WEB_CLIENT_ID,
onSuccess = { idToken ->
eventSink(
LoginUiEvent.Login(
providerType = LoginUiEvent.PROVIDER_TYPE_GOOGLE,
token = idToken,
),
)
},
onFailure = { errorMessage ->
eventSink(LoginUiEvent.LoginFailure(errorMessage))
},
)
}
}

is LoginSideEffect.ShowToast -> {
Toast.makeText(context, state.sideEffect.message, Toast.LENGTH_SHORT).show()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ class LoginPresenter(
sideEffect = LoginSideEffect.KakaoLogin()
}

is LoginUiEvent.OnGoogleLoginButtonClick -> {
isLoading = true
sideEffect = LoginSideEffect.GoogleLogin()
}

is LoginUiEvent.LoginFailure -> {
isLoading = false
analyticsHelper.logEvent(EVENT_ERROR_LOGIN)
Expand All @@ -95,7 +100,7 @@ class LoginPresenter(
scope.launch {
try {
isLoading = true
authRepository.login(event.accessToken)
authRepository.login(event.providerType, event.token)
.onSuccess {
userRepository.syncFcmToken()
navigateAfterLogin()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,30 @@ internal fun LoginUi(
)
},
)
Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing2))
ReedButton(
onClick = {
state.eventSink(LoginUiEvent.OnGoogleLoginButtonClick)
},
sizeStyle = largeButtonStyle,
colorStyle = ReedButtonColorStyle.GOOGLE,
modifier = Modifier
.fillMaxWidth()
.padding(
start = ReedTheme.spacing.spacing5,
end = ReedTheme.spacing.spacing5,
),
text = stringResource(id = R.string.google_login),
leadingIcon = {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_google),
contentDescription = "Google Icon",
tint = Color.Unspecified,
)
},
)
Spacer(
modifier = Modifier.height(if (state.returnToScreen == null) ReedTheme.spacing.spacing2 else ReedTheme.spacing.spacing8),
modifier = Modifier.height(if (state.returnToScreen == null) ReedTheme.spacing.spacing3 else ReedTheme.spacing.spacing8),
)
if (state.returnToScreen == null) {
ReedTextButton(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ data class LoginUiState(
@Immutable
sealed interface LoginSideEffect {
data class KakaoLogin(private val key: String = UUID.randomUUID().toString()) : LoginSideEffect
data class GoogleLogin(private val key: String = UUID.randomUUID().toString()) : LoginSideEffect
data class ShowToast(
val message: String,
private val key: String = UUID.randomUUID().toString(),
Expand All @@ -24,8 +25,17 @@ sealed interface LoginSideEffect {

sealed interface LoginUiEvent : CircuitUiEvent {
data object OnKakaoLoginButtonClick : LoginUiEvent
data class Login(val accessToken: String) : LoginUiEvent
data object OnGoogleLoginButtonClick : LoginUiEvent
data class Login(
val providerType: String,
val token: String,
) : LoginUiEvent
data class LoginFailure(val message: String) : LoginUiEvent
data object OnGuestLoginButtonClick : LoginUiEvent
data object OnCloseButtonClick : LoginUiEvent

companion object {
const val PROVIDER_TYPE_KAKAO = "KAKAO"
const val PROVIDER_TYPE_GOOGLE = "GOOGLE"
}
}
Loading
Loading