diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 12f04c0d..6cfab516 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -115,7 +115,7 @@
+ android:scheme="kakao698fe2d5716b72acfef2760ff5ccd46e" />
diff --git a/app/src/main/java/com/sopt/geonppang/data/datasource/local/GPDataSource.kt b/app/src/main/java/com/sopt/geonppang/data/datasource/local/GPDataSource.kt
index 6921b5d1..506182c4 100644
--- a/app/src/main/java/com/sopt/geonppang/data/datasource/local/GPDataSource.kt
+++ b/app/src/main/java/com/sopt/geonppang/data/datasource/local/GPDataSource.kt
@@ -52,6 +52,13 @@ class GPDataSource @Inject constructor(@ApplicationContext context: Context) {
set(value) = dataStore.edit { putBoolean(IS_LOGIN, value) }
get() = dataStore.getBoolean(IS_LOGIN, false)
+ var userRoleType: String
+ set(value) = dataStore.edit { putString(USER_ROLE_TYPE, value) }
+ get() = dataStore.getString(
+ USER_ROLE_TYPE,
+ ""
+ ) ?: ""
+
fun clear() {
dataStore.edit {
clear()
@@ -64,5 +71,6 @@ class GPDataSource @Inject constructor(@ApplicationContext context: Context) {
const val ACCESS_TOKEN = "AccessToken"
const val REFRESH_TOKEN = "RefreshToken"
const val IS_LOGIN = "IsLogin"
+ const val USER_ROLE_TYPE = "UserRoleType"
}
}
diff --git a/app/src/main/java/com/sopt/geonppang/data/interceptor/AuthInterceptor.kt b/app/src/main/java/com/sopt/geonppang/data/interceptor/AuthInterceptor.kt
index c858115e..e92ff4e9 100644
--- a/app/src/main/java/com/sopt/geonppang/data/interceptor/AuthInterceptor.kt
+++ b/app/src/main/java/com/sopt/geonppang/data/interceptor/AuthInterceptor.kt
@@ -17,21 +17,27 @@ class AuthInterceptor @Inject constructor(
private val context: Application,
) : Interceptor {
+ // TODO dana 경우에 따른 분기 처리 필요
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val authRequest =
originalRequest.newBuilder().addHeader("Authorization", gpDataSource.accessToken)
.build()
- val response = chain.proceed(authRequest)
+ val response = chain.proceed(
+ if (gpDataSource.accessToken.isNotBlank()) {
+ authRequest
+ } else {
+ originalRequest
+ }
+ )
when (response.code) {
401 -> {
response.close()
- val refreshTokenRequest = originalRequest.newBuilder().get()
- .url("${BuildConfig.GP_BASE_URL}auth/refresh")
- .addHeader(ACCESS_TOKEN, gpDataSource.accessToken)
- .addHeader(REFRESH_TOKEN, gpDataSource.refreshToken)
- .build()
+ val refreshTokenRequest =
+ originalRequest.newBuilder().get().url("${BuildConfig.GP_BASE_URL}auth/refresh")
+ .addHeader(ACCESS_TOKEN, gpDataSource.accessToken)
+ .addHeader(REFRESH_TOKEN, gpDataSource.refreshToken).build()
val refreshTokenResponse = chain.proceed(refreshTokenRequest)
if (refreshTokenResponse.isSuccessful) {
@@ -46,15 +52,16 @@ class AuthInterceptor @Inject constructor(
refreshTokenResponse.close()
val newRequest = originalRequest.newBuilder()
- .addHeader(ACCESS_TOKEN, gpDataSource.accessToken)
- .build()
+ .addHeader(ACCESS_TOKEN, gpDataSource.accessToken).build()
return chain.proceed(newRequest)
} else {
with(context) {
CoroutineScope(Dispatchers.Main).launch {
startActivity(
- Intent(this@with, SignActivity::class.java)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ Intent(
+ this@with,
+ SignActivity::class.java
+ ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
)
}
}
diff --git a/app/src/main/java/com/sopt/geonppang/presentation/auth/AuthViewModel.kt b/app/src/main/java/com/sopt/geonppang/presentation/auth/AuthViewModel.kt
index d7dc02a9..da8129c5 100644
--- a/app/src/main/java/com/sopt/geonppang/presentation/auth/AuthViewModel.kt
+++ b/app/src/main/java/com/sopt/geonppang/presentation/auth/AuthViewModel.kt
@@ -11,6 +11,7 @@ import com.sopt.geonppang.domain.repository.AuthRepository
import com.sopt.geonppang.domain.repository.ValidationRepository
import com.sopt.geonppang.presentation.type.AuthRoleType
import com.sopt.geonppang.presentation.type.PlatformType
+import com.sopt.geonppang.presentation.type.UserRoleType
import com.sopt.geonppang.util.UiState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -85,6 +86,9 @@ class AuthViewModel @Inject constructor(
isValidNickname && isNicknameUsable
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
+ val platformType = gpDataSource.platformType
+
+ // TODO: 초기화 부분 고민
fun initNickname() {
_isNicknameUsable.value = UiState.Empty
}
@@ -112,14 +116,11 @@ class AuthViewModel @Inject constructor(
.onSuccess {
if (it.code == 200) {
_isNicknameUsable.value = UiState.Success(true)
- Timber.tag("isNicknameDuplicated")
- .e("{" + it.message + " " + _isNicknameUsable.value + "}")
}
}
.onFailure { throwable ->
Timber.e(throwable.message)
_isNicknameUsable.value = UiState.Error("false")
- Timber.tag("isNicknameNotDuplicate").e("{" + _isNicknameUsable.value + "}")
}
}
}
@@ -128,6 +129,8 @@ class AuthViewModel @Inject constructor(
gpDataSource.isLogin = true
}
+ // 카카오 로그인 이거나, 자체 회원 가입인 경우는 -> role이 USER
+ // 카카오 회원 가입인 경우만 -> role 이 GUEST -> 이 경우메만 닉네임 설정 뷰로 이동
fun signUp(
platformType: PlatformType,
platformToken: String,
@@ -151,13 +154,24 @@ class AuthViewModel @Inject constructor(
val responseHeader = signUpResponse.headers()
val accessToken = responseHeader[AUTHORIZATION].toString()
val refreshToken = responseHeader[AUTHORIZATION_REFRESH].toString()
+
_authRoleType.value =
- if (responseBody?.role == AuthRoleType.GUEST.name) AuthRoleType.GUEST else AuthRoleType.USER
+ if (responseBody?.role == AuthRoleType.ROLE_GUEST.name) {
+ AuthRoleType.ROLE_GUEST
+ } else {
+ AuthRoleType.ROLE_MEMBER
+ }
+
gpDataSource.accessToken = BEARER_PREFIX + accessToken
- if (_authRoleType.value == AuthRoleType.USER) {
+
+ // 카카오 로그인, 자체 회원 가입인 경우메만 리프래시 토큰을 저장하고 회원가입 상태를 success로 지정
+ if (_authRoleType.value == AuthRoleType.ROLE_MEMBER) {
gpDataSource.refreshToken = BEARER_PREFIX + refreshToken
+
+ _signUpState.value = UiState.Success(true)
}
- _signUpState.value = UiState.Success(true)
+
+ // amplitude를 쏘기 위한 것
if (platformType == PlatformType.NONE) {
_memberId.value = responseBody?.memberId
}
@@ -168,22 +182,25 @@ class AuthViewModel @Inject constructor(
}
}
+ // 소셜 회원가입 후에만 사용하는 api
fun settingNickName() {
viewModelScope.launch {
- nickname.value?.let { nickname ->
+ nickname.value.let { nickname ->
authRepository.settingNickname(RequestNicknameSetting(nickname))
.onSuccess { response ->
val responseHeader = response.headers()
val responseBody = response.body()
+
+ // 소셜 회원가입 시 여기서 다시 엑세스, 리프래시 토큰을 발급 받음
val accessToken = responseHeader[AUTHORIZATION].toString()
val refreshToken = responseHeader[AUTHORIZATION_REFRESH].toString()
+
gpDataSource.accessToken = BEARER_PREFIX + accessToken
gpDataSource.refreshToken = BEARER_PREFIX + refreshToken
_signUpState.value = UiState.Success(true)
- // 소셜 회원가입 시 자동 로그인 설정
- setAutoLogin()
_memberId.value = responseBody?.data?.memberId
+ gpDataSource.userRoleType = UserRoleType.FILTER_UNSELECTED_MEMBER.name
}
.onFailure { throwable ->
Timber.e(throwable.message)
diff --git a/app/src/main/java/com/sopt/geonppang/presentation/auth/SignActivity.kt b/app/src/main/java/com/sopt/geonppang/presentation/auth/SignActivity.kt
index d182f891..2923a950 100644
--- a/app/src/main/java/com/sopt/geonppang/presentation/auth/SignActivity.kt
+++ b/app/src/main/java/com/sopt/geonppang/presentation/auth/SignActivity.kt
@@ -13,7 +13,9 @@ import com.sopt.geonppang.presentation.MainActivity
import com.sopt.geonppang.presentation.login.LoginActivity
import com.sopt.geonppang.presentation.type.AuthRoleType
import com.sopt.geonppang.presentation.type.PlatformType
+import com.sopt.geonppang.presentation.type.UserRoleType
import com.sopt.geonppang.util.AmplitudeUtils
+import com.sopt.geonppang.util.UiState
import com.sopt.geonppang.util.binding.BindingActivity
import com.sopt.geonppang.util.extension.setOnSingleClickListener
import dagger.hilt.android.AndroidEntryPoint
@@ -45,17 +47,25 @@ class SignActivity :
moveToSignUp()
AmplitudeUtils.trackEventWithProperties(START_SIGNUP, SIGNUP_TYPE, EMAIL)
}
+
+ // TODO: dana 둘러보기 코드 작성 후 지우기
+ binding.ivLogoText.setOnSingleClickListener {
+ GPDataSource(this).userRoleType = UserRoleType.NONE_MEMBER.name
+ moveToMain()
+ }
}
private fun collectData() {
+ // 카카오 회원 가입, 로그인
authViewModel.authRoleType.flowWithLifecycle(lifecycle).onEach { role ->
when (role) {
- AuthRoleType.GUEST -> {
+ // 카카오 회원가입인 경우 닉네임 페이지로 이동
+ AuthRoleType.ROLE_GUEST -> {
moveToNickNameSetting()
}
- AuthRoleType.USER -> {
- authViewModel.setAutoLogin()
+ // 카카오 로그인인 경우 홈으로 이동
+ AuthRoleType.ROLE_MEMBER -> {
AmplitudeUtils.trackEvent(LOGIN_APP)
moveToMain()
}
@@ -63,6 +73,16 @@ class SignActivity :
else -> {}
}
}.launchIn(lifecycleScope)
+
+ authViewModel.signUpState.flowWithLifecycle(lifecycle).onEach { signUpState ->
+ when (signUpState) {
+ is UiState.Success -> {
+ authViewModel.setAutoLogin()
+ }
+
+ else -> {}
+ }
+ }.launchIn(lifecycleScope)
}
private fun moveToSignUp() {
diff --git a/app/src/main/java/com/sopt/geonppang/presentation/auth/SignUpNicknameActivity.kt b/app/src/main/java/com/sopt/geonppang/presentation/auth/SignUpNicknameActivity.kt
index 39f24817..bc03c02e 100644
--- a/app/src/main/java/com/sopt/geonppang/presentation/auth/SignUpNicknameActivity.kt
+++ b/app/src/main/java/com/sopt/geonppang/presentation/auth/SignUpNicknameActivity.kt
@@ -6,7 +6,6 @@ import androidx.activity.viewModels
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import com.sopt.geonppang.R
-import com.sopt.geonppang.data.datasource.local.GPDataSource
import com.sopt.geonppang.databinding.ActivitySignupNicknameBinding
import com.sopt.geonppang.presentation.type.PlatformType
import com.sopt.geonppang.util.AmplitudeUtils
@@ -50,6 +49,7 @@ class SignUpNicknameActivity :
viewModel.signUpState.flowWithLifecycle(lifecycle).onEach {
when (it) {
is UiState.Success -> {
+ // 회원가입 성공시에만 자동 로그인 설정 (소셜 회원가입도 닉네임까지 설정이 완료된 시점에)
viewModel.setAutoLogin()
AmplitudeUtils.trackEventWithProperties(
COMPLETE_NICKNAME,
@@ -83,6 +83,7 @@ class SignUpNicknameActivity :
viewModel.initNickname()
}
}.launchIn(lifecycleScope)
+
viewModel.memberId.flowWithLifecycle(lifecycle).onEach { memberId ->
if (memberId != null)
AmplitudeUtils.setUserId(GUNBBANG + memberId)
@@ -90,12 +91,13 @@ class SignUpNicknameActivity :
}
private fun completeSignUp() {
- val gpDataSource = GPDataSource(this)
- when (gpDataSource.platformType) {
+ when (viewModel.platformType) {
+ // 카카오 회원가입
PlatformType.KAKAO.name -> {
viewModel.settingNickName()
}
+ // 자체 회원가입
PlatformType.NONE.name -> {
val email = intent.getStringExtra(EMAIL)
val password = intent.getStringExtra(PASSWORD)
diff --git a/app/src/main/java/com/sopt/geonppang/presentation/filterSetting/FilterSettingViewModel.kt b/app/src/main/java/com/sopt/geonppang/presentation/filterSetting/FilterSettingViewModel.kt
index 16c772bf..24f48561 100644
--- a/app/src/main/java/com/sopt/geonppang/presentation/filterSetting/FilterSettingViewModel.kt
+++ b/app/src/main/java/com/sopt/geonppang/presentation/filterSetting/FilterSettingViewModel.kt
@@ -10,6 +10,7 @@ import com.sopt.geonppang.presentation.type.BreadFilterType
import com.sopt.geonppang.presentation.type.FilterInfoType
import com.sopt.geonppang.presentation.type.MainPurposeFilterType
import com.sopt.geonppang.presentation.type.NutrientFilterType
+import com.sopt.geonppang.presentation.type.UserRoleType
import com.sopt.geonppang.util.UiState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -23,7 +24,7 @@ import javax.inject.Inject
@HiltViewModel
class FilterSettingViewModel @Inject constructor(
- gpDataSource: GPDataSource,
+ private val gpDataSource: GPDataSource,
private val filterRepository: FilterSettingRepository
) : ViewModel() {
private val _selectedFilterState =
@@ -100,6 +101,10 @@ class FilterSettingViewModel @Inject constructor(
ingredientType = _nutrientFilterType.value,
)
)
+
+ // 필터를 설정할 때, userRoleType을 selectedMember로 설정 하기
+ // TODO: dana filter 설정할 때마다 재설정하는게 맞는가,,
+ gpDataSource.userRoleType = UserRoleType.FILTER_SELECTED_MEMBER.name
}
.onFailure { throwable ->
_selectedFilterState.value = UiState.Error(throwable.message)
diff --git a/app/src/main/java/com/sopt/geonppang/presentation/home/HomeFragment.kt b/app/src/main/java/com/sopt/geonppang/presentation/home/HomeFragment.kt
index 37651cc3..de4a33a5 100644
--- a/app/src/main/java/com/sopt/geonppang/presentation/home/HomeFragment.kt
+++ b/app/src/main/java/com/sopt/geonppang/presentation/home/HomeFragment.kt
@@ -14,6 +14,7 @@ import com.sopt.geonppang.presentation.detail.DetailActivity.Companion.VIEW_DETA
import com.sopt.geonppang.presentation.filterSetting.FilterSettingActivity
import com.sopt.geonppang.presentation.search.SearchActivity
import com.sopt.geonppang.presentation.type.FilterInfoType
+import com.sopt.geonppang.presentation.type.UserRoleType
import com.sopt.geonppang.util.AmplitudeUtils
import com.sopt.geonppang.util.UiState
import com.sopt.geonppang.util.binding.BindingFragment
@@ -84,13 +85,24 @@ class HomeFragment : BindingFragment(R.layout.fragment_home
}
}.launchIn(lifecycleScope)
- viewModel.isFilterSelected.flowWithLifecycle(lifecycle).onEach { isFilterSelected ->
- binding.tvHomeBestBakeryTitle1.setVisibility(isFilterSelected)
- binding.tvHomeBestReviewTitle1.setVisibility(isFilterSelected)
- binding.includeHomeSpeechBubble.root.setVisibility(!isFilterSelected)
+ // 유저 상태에 따른 ui 분기 처리
+ viewModel.userRoleType.flowWithLifecycle(lifecycle).onEach {
+ when (it) {
+ UserRoleType.NONE_MEMBER.name -> {
+ viewModel.setNickName("별사탕")
+ }
+
+ UserRoleType.FILTER_SELECTED_MEMBER.name -> {
+ binding.tvHomeBestBakeryTitle1.setVisibility(true)
+ binding.tvHomeBestReviewTitle1.setVisibility(true)
+ }
- if (isFilterSelected != null)
- viewModel.fetchBestBakeryList()
+ UserRoleType.FILTER_UNSELECTED_MEMBER.name -> {
+ binding.includeHomeSpeechBubble.root.setVisibility(true)
+ }
+
+ else -> {}
+ }
}.launchIn(lifecycleScope)
}
diff --git a/app/src/main/java/com/sopt/geonppang/presentation/home/HomeViewModel.kt b/app/src/main/java/com/sopt/geonppang/presentation/home/HomeViewModel.kt
index d55bbb04..842b8178 100644
--- a/app/src/main/java/com/sopt/geonppang/presentation/home/HomeViewModel.kt
+++ b/app/src/main/java/com/sopt/geonppang/presentation/home/HomeViewModel.kt
@@ -2,10 +2,12 @@ package com.sopt.geonppang.presentation.home
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import com.sopt.geonppang.data.datasource.local.GPDataSource
import com.sopt.geonppang.domain.model.BestBakery
import com.sopt.geonppang.domain.model.BestReview
import com.sopt.geonppang.domain.repository.GetUserFilterRepository
import com.sopt.geonppang.domain.repository.HomeRepository
+import com.sopt.geonppang.presentation.type.UserRoleType
import com.sopt.geonppang.util.UiState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -16,6 +18,7 @@ import javax.inject.Inject
@HiltViewModel
class HomeViewModel @Inject constructor(
+ private val gpDataSource: GPDataSource,
private val homeRepository: HomeRepository,
private val getUserFilterRepository: GetUserFilterRepository
) : ViewModel() {
@@ -24,30 +27,33 @@ class HomeViewModel @Inject constructor(
private var _bestReviewListState = MutableStateFlow>>(UiState.Loading)
val bestReviewListState get() = _bestReviewListState.asStateFlow()
+
private val _nickName = MutableStateFlow("")
val nickName get() = _nickName.asStateFlow()
- private val _isFilterSelected = MutableStateFlow(true)
- val isFilterSelected get() = _isFilterSelected.asStateFlow()
+ private val _userRoleType = MutableStateFlow(gpDataSource.userRoleType)
+ val userRoleType get() = _userRoleType.asStateFlow()
+
+ // TODO: 무슨 회원인지 로컬 에서 관리
init {
- getUserFilter()
+ // 비회원이 아닌 경우에만 호출 되도록
+ if (_userRoleType.value != UserRoleType.NONE_MEMBER.name) {
+ getUserFilter()
+ }
+ fetchBestList()
}
- fun fetchBestBakeryList() {
+ // 하나의 스코프를 만들어서 각 함수들이 동기적으로 처리되도록 수정
+ fun fetchBestList() {
viewModelScope.launch {
homeRepository.fetchBestBakery()
.onSuccess { bestBakeryList ->
_bestBakeryListState.value = UiState.Success(bestBakeryList)
- fetchBestReviewList()
}
.onFailure { throwable ->
_bestBakeryListState.value = UiState.Error(throwable.message)
}
- }
- }
- private fun fetchBestReviewList() {
- viewModelScope.launch {
homeRepository.fetchBestReview()
.onSuccess { bestReviewList ->
_bestReviewListState.value = UiState.Success(bestReviewList)
@@ -58,12 +64,23 @@ class HomeViewModel @Inject constructor(
}
}
+ // 유저가 필터를 선택 했는지 안 했는지를 구분 하기 위한 api 호출
private fun getUserFilter() {
viewModelScope.launch {
getUserFilterRepository.getUserFilter()
.onSuccess { userFilterInfo ->
- _nickName.value = userFilterInfo.nickName
- _isFilterSelected.value = (userFilterInfo.mainPurpose != NONE)
+ setNickName(userFilterInfo.nickName)
+
+ // 홈에서 필터 조회를 해서 유저 상태 설정
+ val userRoleType =
+ if (userFilterInfo.mainPurpose == NONE) {
+ UserRoleType.FILTER_UNSELECTED_MEMBER.name
+ } else {
+ UserRoleType.FILTER_SELECTED_MEMBER.name
+ }
+
+ gpDataSource.userRoleType = userRoleType
+ _userRoleType.value = userRoleType
}
.onFailure { throwable ->
Timber.e(throwable.message)
@@ -71,6 +88,10 @@ class HomeViewModel @Inject constructor(
}
}
+ fun setNickName(nickName: String) {
+ _nickName.value = nickName
+ }
+
companion object {
const val NONE = "NONE"
}
diff --git a/app/src/main/java/com/sopt/geonppang/presentation/type/AuthRoleType.kt b/app/src/main/java/com/sopt/geonppang/presentation/type/AuthRoleType.kt
index be2c3efb..1922a491 100644
--- a/app/src/main/java/com/sopt/geonppang/presentation/type/AuthRoleType.kt
+++ b/app/src/main/java/com/sopt/geonppang/presentation/type/AuthRoleType.kt
@@ -1,5 +1,8 @@
package com.sopt.geonppang.presentation.type
+// authors: dana
+// description: 회원가입 시 닉네임 설정까지 완료한 유저인지를 구분하기 위한 role Type
+
enum class AuthRoleType {
- USER, GUEST
+ ROLE_MEMBER, ROLE_GUEST
}
diff --git a/app/src/main/java/com/sopt/geonppang/presentation/type/UserRoleType.kt b/app/src/main/java/com/sopt/geonppang/presentation/type/UserRoleType.kt
new file mode 100644
index 00000000..9dbce461
--- /dev/null
+++ b/app/src/main/java/com/sopt/geonppang/presentation/type/UserRoleType.kt
@@ -0,0 +1,8 @@
+package com.sopt.geonppang.presentation.type
+
+// authors: dana
+// description: 앱 내에서 필터 선택 / 필터 선택 안한 / 비회원 상태를 구분 하기 위한 role Type
+
+enum class UserRoleType {
+ FILTER_SELECTED_MEMBER, FILTER_UNSELECTED_MEMBER, NONE_MEMBER
+}
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
index 0a707b3c..237eee70 100644
--- a/app/src/main/res/layout/fragment_home.xml
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -105,6 +105,7 @@
android:layout_height="wrap_content"
android:text="@{@string/home_best_title1(viewModel.nickName)}"
android:textAppearance="@style/TextAppearance.Title2"
+ android:visibility="gone"
tools:text="바이블님 맞춤" />