diff --git a/core/common-ui/src/main/java/com/puzzle/common/ui/CoroutineUtil.kt b/core/common-ui/src/main/java/com/puzzle/common/ui/CoroutineUtil.kt
new file mode 100644
index 00000000..6d989fd6
--- /dev/null
+++ b/core/common-ui/src/main/java/com/puzzle/common/ui/CoroutineUtil.kt
@@ -0,0 +1,14 @@
+package com.puzzle.common.ui
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+fun LifecycleOwner.repeatOnStarted(block: suspend CoroutineScope.() -> Unit) {
+ lifecycleScope.launch {
+ lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED, block)
+ }
+}
\ No newline at end of file
diff --git a/core/designsystem/src/main/java/com/puzzle/designsystem/component/Button.kt b/core/designsystem/src/main/java/com/puzzle/designsystem/component/Button.kt
index ca27a031..b95d8818 100644
--- a/core/designsystem/src/main/java/com/puzzle/designsystem/component/Button.kt
+++ b/core/designsystem/src/main/java/com/puzzle/designsystem/component/Button.kt
@@ -158,6 +158,7 @@ fun PieceLoginButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
+ border: BorderStroke? = null,
containerColor: Color = PieceTheme.colors.primaryDefault,
labelColor: Color = PieceTheme.colors.black
) {
@@ -165,6 +166,7 @@ fun PieceLoginButton(
onClick = onClick,
enabled = enabled,
shape = RoundedCornerShape(8.dp),
+ border = border,
colors = ButtonDefaults.buttonColors(
containerColor = containerColor,
contentColor = PieceTheme.colors.white,
diff --git a/core/designsystem/src/main/res/values/strings.xml b/core/designsystem/src/main/res/values/strings.xml
index b9f343bd..18ff0f04 100644
--- a/core/designsystem/src/main/res/values/strings.xml
+++ b/core/designsystem/src/main/res/values/strings.xml
@@ -16,6 +16,7 @@
매칭 수락하기
+ 서로의 빈 곳을 채우며 맞물리는 퍼즐처럼.\n서로의 가치관과 마음이 연결되는 순간을 만들어갑니다.
카카오로 시작하기
구글로 시작하기
diff --git a/feature/auth/build.gradle.kts b/feature/auth/build.gradle.kts
index d139dad7..fb851e0b 100644
--- a/feature/auth/build.gradle.kts
+++ b/feature/auth/build.gradle.kts
@@ -7,5 +7,6 @@ android {
}
dependencies {
+ implementation(projects.core.common)
implementation(libs.kakao.user)
}
diff --git a/feature/auth/src/main/java/com/puzzle/auth/graph/login/LoginScreen.kt b/feature/auth/src/main/java/com/puzzle/auth/graph/login/LoginScreen.kt
index cca8ef2f..51c16631 100644
--- a/feature/auth/src/main/java/com/puzzle/auth/graph/login/LoginScreen.kt
+++ b/feature/auth/src/main/java/com/puzzle/auth/graph/login/LoginScreen.kt
@@ -1,19 +1,19 @@
package com.puzzle.auth.graph.login
+import android.content.Context
+import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
-import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-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.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -26,11 +26,14 @@ import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.LocalLifecycleOwner
import com.airbnb.mvrx.compose.collectAsState
import com.airbnb.mvrx.compose.mavericksViewModel
import com.kakao.sdk.user.UserApiClient
import com.puzzle.auth.graph.login.contract.LoginIntent.Navigate
+import com.puzzle.auth.graph.login.contract.LoginSideEffect
import com.puzzle.auth.graph.login.contract.LoginState
+import com.puzzle.common.ui.repeatOnStarted
import com.puzzle.designsystem.R
import com.puzzle.designsystem.component.PieceLoginButton
import com.puzzle.designsystem.component.PieceSubCloseTopBar
@@ -40,29 +43,32 @@ import com.puzzle.navigation.AuthGraphDest
import com.puzzle.navigation.NavigationEvent
@Composable
-fun LoginRoute(
+internal fun LoginRoute(
viewModel: LoginViewModel = mavericksViewModel(),
) {
val state by viewModel.collectAsState()
val context = LocalContext.current
+ val lifecycleOwner = LocalLifecycleOwner.current
- LoginScreen(
- state = state,
- loginKakao = {
- UserApiClient.instance.loginWithKakaoAccount(context) { token, error ->
- if (error != null) {
-// Log.e("test", "로그인 실패", error)
- } else if (token != null) {
-// Log.i("test", "로그인 성공 ${token.accessToken}")
+ LaunchedEffect(Unit) {
+ lifecycleOwner.repeatOnStarted {
+ viewModel.sideEffects.collect { sideEffect ->
+ when (sideEffect) {
+ is LoginSideEffect.LoginKakao -> loginKakao(context)
}
}
- },
+ }
+ }
+
+ LoginScreen(
+ state = state,
+ loginKakao = { viewModel.onSideEffect(LoginSideEffect.LoginKakao) },
navigate = { viewModel.onIntent(Navigate(it)) },
)
}
@Composable
-fun LoginScreen(
+private fun LoginScreen(
state: LoginState,
loginKakao: () -> Unit,
navigate: (NavigationEvent) -> Unit,
@@ -85,13 +91,9 @@ fun LoginScreen(
PieceSubCloseTopBar(
title = "",
onCloseClick = { },
- modifier = Modifier
- .fillMaxWidth()
- .padding(vertical = 14.dp),
+ modifier = Modifier.fillMaxWidth(),
)
- Spacer(modifier = Modifier.height(20.dp))
-
Text(
text = buildAnnotatedString {
withStyle(style = SpanStyle(color = PieceTheme.colors.primaryDefault)) {
@@ -102,20 +104,20 @@ fun LoginScreen(
},
style = PieceTheme.typography.headingLSB,
color = PieceTheme.colors.black,
- modifier = Modifier.fillMaxWidth(),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = 20.dp, bottom = 12.dp),
)
- Spacer(modifier = Modifier.height(12.dp))
-
Text(
- text = "서로의 빈 곳을 채우며 맞물리는 퍼즐처럼.\n서로의 가치관과 마음이 연결되는 순간을 만들어갑니다.",
+ text = stringResource(R.string.login_description),
style = PieceTheme.typography.bodySM,
color = PieceTheme.colors.dark3,
- modifier = Modifier.fillMaxWidth(),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(bottom = 70.dp),
)
- Spacer(modifier = Modifier.height(70.dp))
-
Image(
painter = painterResource(R.drawable.ic_puzzle1),
contentDescription = null,
@@ -129,34 +131,52 @@ fun LoginScreen(
PieceLoginButton(
label = stringResource(R.string.kakao_login),
imageId = R.drawable.ic_kakao_login,
- onClick = {},
containerColor = Color(0xFFFFE812),
- modifier = Modifier.fillMaxWidth()
+ onClick = loginKakao,
+ modifier = Modifier.fillMaxWidth(),
)
- Spacer(modifier = Modifier.height(10.dp))
-
PieceLoginButton(
label = stringResource(R.string.google_login),
imageId = R.drawable.ic_google_login,
- onClick = {},
containerColor = PieceTheme.colors.white,
+ border = BorderStroke(
+ width = 1.dp,
+ color = PieceTheme.colors.light1,
+ ),
+ onClick = {},
modifier = Modifier
.fillMaxWidth()
- .border(
- width = 1.dp,
- color = PieceTheme.colors.light1,
- shape = RoundedCornerShape(8.dp)
- ),
+ .padding(vertical = 10.dp),
)
+ }
+}
- Spacer(modifier = Modifier.height(10.dp))
+private fun loginKakao(context: Context) {
+ UserApiClient.instance.apply {
+ if (isKakaoTalkLoginAvailable(context)) {
+ loginWithKakaoAccount(context) { token, error ->
+ if (error != null) {
+// Log.e("test", "로그인 실패", error)
+ } else if (token != null) {
+ // Log.i("test", "로그인 성공 ${token.accessToken}")
+ }
+ }
+ } else {
+ loginWithKakaoAccount(context) { token, error ->
+ if (error != null) {
+// Log.e("test", "로그인 실패", error)
+ } else if (token != null) {
+// Log.i("test", "로그인 성공 ${token.accessToken}")
+ }
+ }
+ }
}
}
@Preview
@Composable
-fun PreviewAuthScreen() {
+private fun PreviewAuthScreen() {
PieceTheme {
LoginScreen(
state = LoginState(),
diff --git a/feature/auth/src/main/java/com/puzzle/auth/graph/login/LoginViewModel.kt b/feature/auth/src/main/java/com/puzzle/auth/graph/login/LoginViewModel.kt
index b97089d0..c3e965b5 100644
--- a/feature/auth/src/main/java/com/puzzle/auth/graph/login/LoginViewModel.kt
+++ b/feature/auth/src/main/java/com/puzzle/auth/graph/login/LoginViewModel.kt
@@ -24,8 +24,8 @@ class LoginViewModel @AssistedInject constructor(
) : MavericksViewModel(initialState) {
private val intents = Channel(BUFFERED)
- private val _sideEffect = Channel(BUFFERED)
- val sideEffect = _sideEffect.receiveAsFlow()
+ private val _sideEffects = Channel(BUFFERED)
+ val sideEffects = _sideEffects.receiveAsFlow()
init {
intents.receiveAsFlow()
@@ -43,10 +43,8 @@ class LoginViewModel @AssistedInject constructor(
}
}
- private fun handleSideEffect(sideEffect: LoginSideEffect) {
- when (sideEffect) {
- else -> Unit
- }
+ internal fun onSideEffect(sideEffect: LoginSideEffect) = viewModelScope.launch {
+ _sideEffects.send(sideEffect)
}
@AssistedFactory
diff --git a/feature/auth/src/main/java/com/puzzle/auth/graph/login/contract/LoginSideEffect.kt b/feature/auth/src/main/java/com/puzzle/auth/graph/login/contract/LoginSideEffect.kt
index e05bd407..6854fddf 100644
--- a/feature/auth/src/main/java/com/puzzle/auth/graph/login/contract/LoginSideEffect.kt
+++ b/feature/auth/src/main/java/com/puzzle/auth/graph/login/contract/LoginSideEffect.kt
@@ -1,3 +1,5 @@
package com.puzzle.auth.graph.login.contract
-sealed class LoginSideEffect
\ No newline at end of file
+sealed class LoginSideEffect {
+ data object LoginKakao : LoginSideEffect()
+}
\ No newline at end of file
diff --git a/presentation/src/main/java/com/puzzle/presentation/MainActivity.kt b/presentation/src/main/java/com/puzzle/presentation/MainActivity.kt
index 6057f1d5..a5c3446a 100644
--- a/presentation/src/main/java/com/puzzle/presentation/MainActivity.kt
+++ b/presentation/src/main/java/com/puzzle/presentation/MainActivity.kt
@@ -9,12 +9,11 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.core.view.WindowCompat
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navOptions
import com.puzzle.common.event.PieceEvent
+import com.puzzle.common.ui.repeatOnStarted
import com.puzzle.designsystem.foundation.PieceTheme
import com.puzzle.navigation.MatchingGraph
import com.puzzle.navigation.NavigationEvent
@@ -42,7 +41,7 @@ class MainActivity : ComponentActivity() {
snackBarHostState = remember { SnackbarHostState() }
LaunchedEffect(Unit) {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
+ repeatOnStarted {
launch {
navigationHelper.navigationFlow.collect { event ->
handleNavigationEvent(