Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT/#5] 온보딩 뷰 / 카카오 연결 #13

Merged
merged 22 commits into from
Jul 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9ecb7b0
[ADD/#5] 로그인, 회원가입 화면 추가
leeeyubin Jul 4, 2024
185fda5
[ADD/#5] 카카오 sdk 추가
leeeyubin Jul 4, 2024
ea5ee90
Merge branch 'develop' of https://github.com/teamterning/Terning-Andr…
leeeyubin Jul 4, 2024
0f06850
Merge branch 'develop' of https://github.com/teamterning/Terning-Andr…
leeeyubin Jul 5, 2024
95d0f42
[ADD/#5] 카카오 의존성 추가
leeeyubin Jul 5, 2024
7e34229
[FEAT/#5] NATIVE_APP_KEY 구현
leeeyubin Jul 5, 2024
ee4abf8
[FEAT/#5] kakaoVersion Update
leeeyubin Jul 5, 2024
a08222f
[ADD/#5] add signin, signup, filtering screen
leeeyubin Jul 5, 2024
4156194
[ADD/#5] add signin, signup navigation
leeeyubin Jul 5, 2024
de120c9
[UI/#5] KakaoButton UI 구현
leeeyubin Jul 5, 2024
552b34f
[FEAT/#5] 카카오톡 소셜 로그인 구현
leeeyubin Jul 6, 2024
8b3b34b
[FEAT/#5] SignInViewModel 함수화
leeeyubin Jul 6, 2024
350c569
[CHORE/#5] 코드 수정
leeeyubin Jul 6, 2024
ac8dd67
[CHORE/#5] nonnull로 수정
leeeyubin Jul 6, 2024
47aa109
[CHORE/#5] 불필요한 코드 삭제
leeeyubin Jul 6, 2024
f8b4ab8
[CHORE/#5] 컨플릭 수정
leeeyubin Jul 6, 2024
3540521
[FIX/#5] 컨플릭 수정
leeeyubin Jul 6, 2024
4bcb604
[FIX/#5] 무한 로딩 수정
leeeyubin Jul 6, 2024
1110416
[FIX/#5] 코드 수정
leeeyubin Jul 6, 2024
2dc3f98
[CHORE/#5] NavigateToHome 네이밍 수정
leeeyubin Jul 6, 2024
da0b647
[CHORE/#5] startDestination SignIn -> Home
leeeyubin Jul 6, 2024
a8449f0
[CHORE/#5] startDestination Home -> SignIn
leeeyubin Jul 6, 2024
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
12 changes: 12 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ android {
vectorDrawables {
useSupportLibrary = true
}

buildConfigField(
"String",
"NATIVE_APP_KEY",
gradleLocalProperties(rootDir, providers).getProperty("native.app.key"),
)
manifestPlaceholders["NATIVE_APP_KEY"] =
gradleLocalProperties(rootDir, providers).getProperty("nativeAppKey")
}

buildTypes {
Expand Down Expand Up @@ -104,4 +112,8 @@ dependencies {

debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)

// KakaoDependencies
implementation(libs.kakao.user)

}
6 changes: 6 additions & 0 deletions app/src/main/java/com/terning/point/MyApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.terning.point

import android.app.Application
import androidx.appcompat.app.AppCompatDelegate
import com.kakao.sdk.common.KakaoSdk
import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber

Expand All @@ -13,6 +14,7 @@ class MyApp : Application() {

initTimber()
setDayMode()
initKakoSdk()
}

private fun initTimber() {
Expand All @@ -22,4 +24,8 @@ class MyApp : Application() {
private fun setDayMode() {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
}

private fun initKakoSdk() {
KakaoSdk.init(this, BuildConfig.NATIVE_APP_KEY)
}
}
3 changes: 0 additions & 3 deletions core/src/main/java/com/terning/core/extension/ContextExt.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package com.terning.core.extension

import android.app.Activity
import android.content.Context
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.annotation.StringRes

Expand Down
3 changes: 3 additions & 0 deletions feature/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,7 @@ dependencies {
implementation(libs.ossLicense)
implementation(libs.lottie)

// KakaoDependencies
implementation(libs.kakao.user)

}
16 changes: 16 additions & 0 deletions feature/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,21 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity
android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="oauth"
android:scheme="kakao${NATIVE_APP_KEY}" />
</intent-filter>
</activity>

</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navOptions
import com.terning.feature.calendar.navigation.navigateCalendar
import com.terning.feature.home.navigation.Home
import com.terning.feature.home.navigation.navigateHome
import com.terning.feature.mypage.navigation.navigateMyPage
import com.terning.feature.onboarding.signin.navigation.SignIn
import com.terning.feature.search.navigation.navigateSearch

class MainNavigator(
Expand All @@ -22,7 +22,7 @@ class MainNavigator(
@Composable get() = navController
.currentBackStackEntryAsState().value?.destination

val startDestination = Home
val startDestination = SignIn

val currentTab: MainTab?
@Composable get() = MainTab.find { tab ->
Expand Down
8 changes: 4 additions & 4 deletions feature/src/main/java/com/terning/feature/main/MainScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalAbsoluteTonalElevation
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
Expand All @@ -27,6 +24,8 @@ import com.terning.core.designsystem.theme.White
import com.terning.feature.calendar.navigation.calendarNavGraph
import com.terning.feature.home.navigation.homeNavGraph
import com.terning.feature.mypage.navigation.myPageNavGraph
import com.terning.feature.onboarding.signin.navigation.signInNavGraph
import com.terning.feature.onboarding.signup.navigation.signUpNavGraph
import com.terning.feature.search.navigation.searchNavGraph
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
Expand Down Expand Up @@ -58,6 +57,8 @@ fun MainScreen(
calendarNavGraph()
searchNavGraph()
myPageNavGraph()
signInNavGraph(navHostController = navigator.navController)
signUpNavGraph()
}
}
}
Expand Down Expand Up @@ -108,7 +109,6 @@ private fun MainBottomBar(
}
}


private object NoRippleInteractionSource : MutableInteractionSource {

override val interactions: Flow<Interaction> = emptyFlow()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fun MyPageRoute(
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current

val state by viewModel.state.collectAsStateWithLifecycle(lifecycleOwner = LocalLifecycleOwner.current)
val state by viewModel.state.collectAsStateWithLifecycle(lifecycleOwner = lifecycleOwner)

LaunchedEffect(key1 = true) {
viewModel.getFriendsInfo(2)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.terning.feature.onboarding.filtering

import androidx.compose.runtime.Composable
import com.terning.feature.onboarding.filtering.navigation.FilteringNavigation

@Composable
fun FilteringRoute(){

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.terning.feature.onboarding.filtering.navigation

class FilteringNavigation {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.terning.feature.onboarding.signin

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
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
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.navigation.NavHostController
import com.terning.core.designsystem.theme.TerningTheme
import com.terning.core.extension.toast
import com.terning.feature.R
import com.terning.feature.home.navigation.navigateHome
import com.terning.feature.onboarding.signin.component.KakaoButton

@Composable
fun SignInRoute(
viewModel: SignInViewModel = hiltViewModel(),
navController: NavHostController,
) {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val signInState by viewModel.signInState.collectAsStateWithLifecycle(lifecycleOwner = lifecycleOwner)

LaunchedEffect(viewModel.signInSideEffects, lifecycleOwner) {
viewModel.signInSideEffects.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle)
.collect { sideEffect ->
when (sideEffect) {
is SignInSideEffect.ShowToast -> context.toast(sideEffect.message)
is SignInSideEffect.NavigateToHome -> navController.navigateHome()
}
}
}

SignInScreen(
onSignInClick = { viewModel.startKakaoLogIn(context) }
)
}

@Composable
fun SignInScreen(
modifier: Modifier = Modifier,
onSignInClick: () -> Unit = {}
) {
Column(
modifier = Modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(id = R.drawable.img_terning_point),
contentDescription = null,
modifier = Modifier
.size(500.dp),
)
KakaoButton(
title = stringResource(id = R.string.sign_in_kakao_button),
Copy link
Contributor

Choose a reason for hiding this comment

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

[where]_[what] 네이밍 컨벤션 다시 한 번 확인합니당!

onSignInClick = { onSignInClick() },
modifier = modifier.padding(horizontal = 20.dp)
)
}
}

@Preview(showBackground = true)
@Composable
fun SignInScreenPreview() {
TerningTheme {
SignInScreen()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.terning.feature.onboarding.signin

import androidx.annotation.StringRes

sealed class SignInSideEffect {
data object NavigateToHome : SignInSideEffect()
data class ShowToast(@StringRes val message: Int) : SignInSideEffect()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.terning.feature.onboarding.signin

import com.terning.core.state.UiState

data class SignInState (
val accessToken: UiState<String> = UiState.Loading
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.terning.feature.onboarding.signin

import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.kakao.sdk.auth.model.OAuthToken
import com.kakao.sdk.common.model.ClientError
import com.kakao.sdk.common.model.ClientErrorCause
import com.kakao.sdk.user.UserApiClient
import com.terning.core.state.UiState
import com.terning.feature.R
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class SignInViewModel @Inject constructor() : ViewModel() {

private val _signInState = MutableStateFlow(SignInState())
val signInState: StateFlow<SignInState>
get() = _signInState.asStateFlow()

private val _signInSideEffects = MutableSharedFlow<SignInSideEffect>()
val signInSideEffects: SharedFlow<SignInSideEffect>
get() = _signInSideEffects.asSharedFlow()

fun startKakaoLogIn(context: Context) {
if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) {
UserApiClient.instance.loginWithKakaoTalk(context) { token, error ->
signInResult(context, token, error)
}
} else {
UserApiClient.instance.loginWithKakaoAccount(context) { token, error ->
signInResult(context, token, error)
}
}
}

private fun signInResult(context: Context, token: OAuthToken?, error: Throwable?) {
viewModelScope.launch {
if (error != null) {
signInFailure(context, error)
} else if (token != null) {
signInSuccess(token)
}
}
}

private fun signInFailure(context: Context, error: Throwable?) {
if (error.toString().contains(KAKAO_NOT_LOGGED_IN)) {
UserApiClient.instance.loginWithKakaoAccount(context) { token, error ->
signInResult(context, token, error)
}
} else {
sigInCancellationOrError(error)
}
}

private fun sigInCancellationOrError(error: Throwable?) {
viewModelScope.launch {
if (error is ClientError && error.reason == ClientErrorCause.Cancelled) {
_signInSideEffects.emit(SignInSideEffect.ShowToast(R.string.sign_in_kakao_cancel))
} else {
_signInSideEffects.emit(SignInSideEffect.ShowToast(R.string.sign_in_kakao_login_fail))
}
}
}

private fun signInSuccess(token: OAuthToken) {
viewModelScope.launch {
_signInState.value =
_signInState.value.copy(accessToken = UiState.Success(token.accessToken))
_signInSideEffects.emit(SignInSideEffect.NavigateToHome)
}
}

companion object {
private const val KAKAO_NOT_LOGGED_IN = "statusCode=302"
}
}
Loading
Loading