diff --git a/app/build.gradle.kts b/app/build.gradle.kts index bf2077f2..4a9e40b0 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -33,7 +33,7 @@ android { gradleLocalProperties(rootDir).getProperty("native.app.key"), ) manifestPlaceholders["NATIVE_APP_KEY"] = - gradleLocalProperties(rootDir).getProperty("native.app.key") + gradleLocalProperties(rootDir).getProperty("nativeAppKey") } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 06886f39..313da73a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,12 +21,11 @@ android:exported="true"> - - + @@ -37,7 +36,7 @@ android:screenOrientation="portrait" /> @@ -48,7 +47,7 @@ @@ -58,7 +57,7 @@ android:screenOrientation="portrait" /> diff --git a/app/src/main/java/com/going/going/di/DataSourceModule.kt b/app/src/main/java/com/going/going/di/DataSourceModule.kt index 9e7eb073..66020dce 100644 --- a/app/src/main/java/com/going/going/di/DataSourceModule.kt +++ b/app/src/main/java/com/going/going/di/DataSourceModule.kt @@ -1,8 +1,8 @@ package com.going.going.di -import com.going.data.datasource.LoginDataSource +import com.going.data.datasource.AuthDataSource import com.going.data.datasource.MockDataSource -import com.going.data.datasourceImpl.LoginDataSourceImpl +import com.going.data.datasourceImpl.AuthDataSourceImpl import com.going.data.datasourceImpl.MockDataSourceImpl import dagger.Module import dagger.Provides @@ -21,8 +21,6 @@ object DataSourceModule { @Provides @Singleton - fun provideLoginDataSource(loginDataSourceImpl: LoginDataSourceImpl): LoginDataSource = - loginDataSourceImpl - - + fun provideAuthDataSource(authDataSourceImpl: AuthDataSourceImpl): AuthDataSource = + authDataSourceImpl } diff --git a/app/src/main/java/com/going/going/di/DataStoreModule.kt b/app/src/main/java/com/going/going/di/DataStoreModule.kt new file mode 100644 index 00000000..52a36091 --- /dev/null +++ b/app/src/main/java/com/going/going/di/DataStoreModule.kt @@ -0,0 +1,27 @@ +package com.going.going.di + +import android.content.Context +import android.content.SharedPreferences +import com.going.data.local.GoingDataStoreImpl +import com.going.data.local.GoingDataStore +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object DataStoreModule { + + @Provides + @Singleton + fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences = + context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE) + + @Provides + @Singleton + fun provideGoingDataStore(dataStoreImpl: GoingDataStoreImpl): GoingDataStore = + dataStoreImpl +} diff --git a/app/src/main/java/com/going/going/di/RepositoryModule.kt b/app/src/main/java/com/going/going/di/RepositoryModule.kt index 54247470..cad8a8ac 100644 --- a/app/src/main/java/com/going/going/di/RepositoryModule.kt +++ b/app/src/main/java/com/going/going/di/RepositoryModule.kt @@ -1,8 +1,10 @@ package com.going.going.di -import com.going.data.repositoryImpl.LoginRepositoryImpl +import com.going.data.repositoryImpl.TokenRepositoryImpl +import com.going.data.repositoryImpl.AuthRepositoryImpl import com.going.data.repositoryImpl.MockRepositoryImpl -import com.going.domain.repository.LoginRepository +import com.going.domain.repository.TokenRepository +import com.going.domain.repository.AuthRepository import com.going.domain.repository.MockRepository import dagger.Module import dagger.Provides @@ -21,6 +23,11 @@ object RepositoryModule { @Provides @Singleton - fun provideLoginRepository(loginRepositoryImpl: LoginRepositoryImpl): LoginRepository = - loginRepositoryImpl + fun provideAuthRepository(authRepositoryImpl: AuthRepositoryImpl): AuthRepository = + authRepositoryImpl + + @Provides + @Singleton + fun provideTokenRepository(tokenRepositoryImpl: TokenRepositoryImpl): TokenRepository = + tokenRepositoryImpl } diff --git a/app/src/main/java/com/going/going/di/ServiceModule.kt b/app/src/main/java/com/going/going/di/ServiceModule.kt index 1f39737b..1e96393d 100644 --- a/app/src/main/java/com/going/going/di/ServiceModule.kt +++ b/app/src/main/java/com/going/going/di/ServiceModule.kt @@ -1,6 +1,6 @@ package com.going.going.di -import com.going.data.service.LoginService +import com.going.data.service.AuthService import com.going.data.service.MockService import dagger.Module import dagger.Provides @@ -20,7 +20,6 @@ object ServiceModule { @Provides @Singleton - fun provideLoginService(retrofit: Retrofit): LoginService = - retrofit.create(LoginService::class.java) - + fun provideLoginService(retrofit: Retrofit): AuthService = + retrofit.create(AuthService::class.java) } diff --git a/core-ui/build.gradle.kts b/core-ui/build.gradle.kts index c9e7e617..eccb92a0 100644 --- a/core-ui/build.gradle.kts +++ b/core-ui/build.gradle.kts @@ -47,4 +47,4 @@ dependencies { testImplementation(TestDependencies.jUnit) androidTestImplementation(TestDependencies.androidTest) androidTestImplementation(TestDependencies.espresso) -} \ No newline at end of file +} diff --git a/data/src/main/java/com/going/data/datasource/AuthDataSource.kt b/data/src/main/java/com/going/data/datasource/AuthDataSource.kt new file mode 100644 index 00000000..e0da7f45 --- /dev/null +++ b/data/src/main/java/com/going/data/datasource/AuthDataSource.kt @@ -0,0 +1,18 @@ +package com.going.data.datasource + +import com.going.data.dto.BaseResponse +import com.going.data.dto.request.SignInRequestDto +import com.going.data.dto.request.SignUpRequestDto +import com.going.data.dto.response.AuthResponseDto + +interface AuthDataSource { + suspend fun postSignIn( + Authorization: String, + platform: SignInRequestDto, + ): BaseResponse + + suspend fun postSignUp( + Authorization: String, + data: SignUpRequestDto, + ): BaseResponse +} diff --git a/data/src/main/java/com/going/data/datasource/LoginDataSource.kt b/data/src/main/java/com/going/data/datasource/LoginDataSource.kt deleted file mode 100644 index 254e1b4c..00000000 --- a/data/src/main/java/com/going/data/datasource/LoginDataSource.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.going.data.datasource - -import com.going.data.dto.BaseResponse -import com.going.data.dto.request.RequestLoginDto -import com.going.data.dto.response.LoginResponseDto - -interface LoginDataSource { - suspend fun postLogin( - Authorization: String, - platform: RequestLoginDto, - ): BaseResponse -} diff --git a/data/src/main/java/com/going/data/datasourceImpl/AuthDataSourceImpl.kt b/data/src/main/java/com/going/data/datasourceImpl/AuthDataSourceImpl.kt new file mode 100644 index 00000000..2c561ab7 --- /dev/null +++ b/data/src/main/java/com/going/data/datasourceImpl/AuthDataSourceImpl.kt @@ -0,0 +1,24 @@ +package com.going.data.datasourceImpl + +import com.going.data.datasource.AuthDataSource +import com.going.data.dto.BaseResponse +import com.going.data.dto.request.SignInRequestDto +import com.going.data.dto.request.SignUpRequestDto +import com.going.data.dto.response.AuthResponseDto +import com.going.data.service.AuthService +import javax.inject.Inject + +class AuthDataSourceImpl @Inject constructor( + private val authService: AuthService, +) : AuthDataSource { + override suspend fun postSignIn( + Authorization: String, + platform: SignInRequestDto, + ): BaseResponse = + authService.postSignin(Authorization, platform) + + override suspend fun postSignUp( + Authorization: String, + data: SignUpRequestDto, + ): BaseResponse = authService.postSignUp(Authorization, data) +} diff --git a/data/src/main/java/com/going/data/datasourceImpl/LoginDataSourceImpl.kt b/data/src/main/java/com/going/data/datasourceImpl/LoginDataSourceImpl.kt deleted file mode 100644 index b2f5c59b..00000000 --- a/data/src/main/java/com/going/data/datasourceImpl/LoginDataSourceImpl.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.going.data.datasourceImpl - -import com.going.data.datasource.LoginDataSource -import com.going.data.dto.BaseResponse -import com.going.data.dto.request.RequestLoginDto -import com.going.data.dto.response.LoginResponseDto -import com.going.data.service.LoginService -import javax.inject.Inject - -class LoginDataSourceImpl @Inject constructor( - private val loginService: LoginService, -) : LoginDataSource { - override suspend fun postLogin( - Authorization: String, - platform: RequestLoginDto, - ): BaseResponse = - loginService.postSignin(Authorization, platform) -} diff --git a/data/src/main/java/com/going/data/dto/request/RequestLoginDto.kt b/data/src/main/java/com/going/data/dto/request/RequestLoginDto.kt deleted file mode 100644 index a10a4310..00000000 --- a/data/src/main/java/com/going/data/dto/request/RequestLoginDto.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.going.data.dto.request - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class RequestLoginDto( - @SerialName("platform") - val platform: String -) diff --git a/data/src/main/java/com/going/data/dto/request/SignInRequestDto.kt b/data/src/main/java/com/going/data/dto/request/SignInRequestDto.kt new file mode 100644 index 00000000..bd982038 --- /dev/null +++ b/data/src/main/java/com/going/data/dto/request/SignInRequestDto.kt @@ -0,0 +1,15 @@ +package com.going.data.dto.request + +import com.going.domain.entity.request.RequestSignInModel +import com.going.domain.entity.request.RequestSignUpModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class SignInRequestDto( + @SerialName("platform") + val platform: String +) + +fun RequestSignInModel.toSignInRequestDto(): SignInRequestDto = + SignInRequestDto(platform) diff --git a/data/src/main/java/com/going/data/dto/request/SignUpRequestDto.kt b/data/src/main/java/com/going/data/dto/request/SignUpRequestDto.kt new file mode 100644 index 00000000..c5f8b525 --- /dev/null +++ b/data/src/main/java/com/going/data/dto/request/SignUpRequestDto.kt @@ -0,0 +1,18 @@ +package com.going.data.dto.request + +import com.going.domain.entity.request.RequestSignUpModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class SignUpRequestDto( + @SerialName("name") + val name: String, + @SerialName("intro") + val intro: String, + @SerialName("platform") + val platform: String, +) + +fun RequestSignUpModel.toSignUpRequestDto(): SignUpRequestDto = + SignUpRequestDto(name, intro, platform) diff --git a/data/src/main/java/com/going/data/dto/response/AuthResponseDto.kt b/data/src/main/java/com/going/data/dto/response/AuthResponseDto.kt new file mode 100644 index 00000000..25cc1041 --- /dev/null +++ b/data/src/main/java/com/going/data/dto/response/AuthResponseDto.kt @@ -0,0 +1,16 @@ +package com.going.data.dto.response + +import com.going.domain.entity.response.AuthTokenModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class AuthResponseDto( + @SerialName("accessToken") + val accessToken: String, + @SerialName("refreshToken") + val refreshToken: String, +) { + fun toAuthTokenModel() = + AuthTokenModel(accessToken = accessToken, refreshToken = refreshToken) +} diff --git a/data/src/main/java/com/going/data/dto/response/LoginResponseDto.kt b/data/src/main/java/com/going/data/dto/response/LoginResponseDto.kt deleted file mode 100644 index c52b73e6..00000000 --- a/data/src/main/java/com/going/data/dto/response/LoginResponseDto.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.going.data.dto.response - -import com.going.domain.entity.response.AuthTokenModel -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class LoginResponseDto( - @SerialName("accessToken") - val accessToken: String, - @SerialName("refreshToken") - val refreshToken: String, -) { - fun toAuthTokenModel() = - AuthTokenModel(accessToken = accessToken, refreshToken = refreshToken) -} - -// @Serializable -// data class LoginResponseDto( -// @SerialName("status") -// val status: Int, -// @SerialName("message") -// val message: String, -// @SerialName("data") -// val data: Data, -// ) { -// @Serializable -// data class Data( -// @SerialName("accessToken") -// val accessToken: String, -// @SerialName("refreshToken") -// val refreshToken: String, -// ) -// -// fun toAuthTokenModel() = -// AuthTokenModel(accessToken = data.accessToken, refreshToken = data.refreshToken) -// } diff --git a/data/src/main/java/com/going/data/local/GoingDataStore.kt b/data/src/main/java/com/going/data/local/GoingDataStore.kt new file mode 100644 index 00000000..e331b14c --- /dev/null +++ b/data/src/main/java/com/going/data/local/GoingDataStore.kt @@ -0,0 +1,6 @@ +package com.going.data.local + +interface GoingDataStore { + var accessToken: String + var refreshToken: String +} diff --git a/data/src/main/java/com/going/data/local/GoingDataStoreImpl.kt b/data/src/main/java/com/going/data/local/GoingDataStoreImpl.kt new file mode 100644 index 00000000..d1de4eb1 --- /dev/null +++ b/data/src/main/java/com/going/data/local/GoingDataStoreImpl.kt @@ -0,0 +1,22 @@ +package com.going.data.local + +import android.content.SharedPreferences +import androidx.core.content.edit +import javax.inject.Inject + +class GoingDataStoreImpl @Inject constructor( + private val dataStore: SharedPreferences, +) : GoingDataStore { + override var accessToken: String + get() = dataStore.getString(ACCESS_TOKEN, "") ?: "" + set(value) = dataStore.edit { putString(ACCESS_TOKEN, value) } + + override var refreshToken: String + get() = dataStore.getString(REFRESH_TOKEN, "") ?: "" + set(value) = dataStore.edit { putString(REFRESH_TOKEN, value) } + + companion object { + private const val ACCESS_TOKEN = "ACCESS_TOKEN" + private const val REFRESH_TOKEN = "REFRESH_TOKEN" + } +} diff --git a/data/src/main/java/com/going/data/repositoryImpl/AuthRepositoryImpl.kt b/data/src/main/java/com/going/data/repositoryImpl/AuthRepositoryImpl.kt new file mode 100644 index 00000000..4aeb29db --- /dev/null +++ b/data/src/main/java/com/going/data/repositoryImpl/AuthRepositoryImpl.kt @@ -0,0 +1,36 @@ +package com.going.data.repositoryImpl + +import com.going.data.datasource.AuthDataSource +import com.going.data.dto.request.toSignInRequestDto +import com.going.data.dto.request.toSignUpRequestDto +import com.going.domain.entity.request.RequestSignInModel +import com.going.domain.entity.request.RequestSignUpModel +import com.going.domain.entity.response.AuthTokenModel +import com.going.domain.repository.AuthRepository +import javax.inject.Inject + +class AuthRepositoryImpl @Inject constructor( + private val authDataSource: AuthDataSource, +) : AuthRepository { + override suspend fun postSignIn( + Authorization: String, + requestSignIpModel: RequestSignInModel, + ): Result = + runCatching { + authDataSource.postSignIn( + Authorization, + requestSignIpModel.toSignInRequestDto(), + ).data.toAuthTokenModel() + } + + override suspend fun postSignUp( + Authorization: String, + requestSignUpModel: RequestSignUpModel, + ): Result = + runCatching { + authDataSource.postSignUp( + Authorization, + requestSignUpModel.toSignUpRequestDto(), + ).data.toAuthTokenModel() + } +} diff --git a/data/src/main/java/com/going/data/repositoryImpl/LoginRepositoryImpl.kt b/data/src/main/java/com/going/data/repositoryImpl/LoginRepositoryImpl.kt deleted file mode 100644 index 04d5e991..00000000 --- a/data/src/main/java/com/going/data/repositoryImpl/LoginRepositoryImpl.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.going.data.repositoryImpl - -import com.going.data.datasource.LoginDataSource -import com.going.data.dto.request.RequestLoginDto -import com.going.domain.entity.response.AuthTokenModel -import com.going.domain.repository.LoginRepository -import javax.inject.Inject - -class LoginRepositoryImpl @Inject constructor( - private val loginDataSource: LoginDataSource, -) : LoginRepository { - override suspend fun postSignin( - Authorization: String, - platform: String, - ): Result = - runCatching { - loginDataSource.postLogin( - Authorization, - RequestLoginDto(platform), - ).data.toAuthTokenModel() - } -} diff --git a/data/src/main/java/com/going/data/repositoryImpl/TokenRepositoryImpl.kt b/data/src/main/java/com/going/data/repositoryImpl/TokenRepositoryImpl.kt new file mode 100644 index 00000000..ba4b219d --- /dev/null +++ b/data/src/main/java/com/going/data/repositoryImpl/TokenRepositoryImpl.kt @@ -0,0 +1,18 @@ +package com.going.data.repositoryImpl + +import com.going.data.local.GoingDataStore +import com.going.domain.repository.TokenRepository +import javax.inject.Inject + +class TokenRepositoryImpl @Inject constructor( + private val goingDataStore: GoingDataStore, +) : TokenRepository { + override fun getAccessToken(): String = goingDataStore.accessToken + + override fun setTokens(accessToken: String, refreshToken: String) { + goingDataStore.accessToken = accessToken + goingDataStore.refreshToken = refreshToken + } + + override fun getRefreshToken(): String = goingDataStore.refreshToken +} diff --git a/data/src/main/java/com/going/data/service/AuthService.kt b/data/src/main/java/com/going/data/service/AuthService.kt new file mode 100644 index 00000000..0102622a --- /dev/null +++ b/data/src/main/java/com/going/data/service/AuthService.kt @@ -0,0 +1,23 @@ +package com.going.data.service + +import com.going.data.dto.BaseResponse +import com.going.data.dto.request.SignInRequestDto +import com.going.data.dto.request.SignUpRequestDto +import com.going.data.dto.response.AuthResponseDto +import retrofit2.http.Body +import retrofit2.http.Header +import retrofit2.http.POST + +interface AuthService { + @POST("api/users/signin") + suspend fun postSignin( + @Header("Authorization") Authorization: String, + @Body body: SignInRequestDto, + ): BaseResponse + + @POST("api/users/signup") + suspend fun postSignUp( + @Header("Authorization") Authorization: String, + @Body body: SignUpRequestDto, + ): BaseResponse +} diff --git a/data/src/main/java/com/going/data/service/LoginService.kt b/data/src/main/java/com/going/data/service/LoginService.kt deleted file mode 100644 index 1859e29b..00000000 --- a/data/src/main/java/com/going/data/service/LoginService.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.going.data.service - -import com.going.data.dto.BaseResponse -import com.going.data.dto.request.RequestLoginDto -import com.going.data.dto.response.LoginResponseDto -import retrofit2.http.Body -import retrofit2.http.Header -import retrofit2.http.POST - -interface LoginService { - @POST("api/users/signin") - suspend fun postSignin( - @Header("Authorization") Authorization: String, - @Body body: RequestLoginDto, - ): BaseResponse -} diff --git a/domain/src/main/kotlin/com/going/domain/entity/request/RequestSignInModel.kt b/domain/src/main/kotlin/com/going/domain/entity/request/RequestSignInModel.kt new file mode 100644 index 00000000..fc88c6ea --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/entity/request/RequestSignInModel.kt @@ -0,0 +1,5 @@ +package com.going.domain.entity.request + +data class RequestSignInModel( + val platform: String +) diff --git a/domain/src/main/kotlin/com/going/domain/entity/request/RequestSignUpModel.kt b/domain/src/main/kotlin/com/going/domain/entity/request/RequestSignUpModel.kt new file mode 100644 index 00000000..17be5dc3 --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/entity/request/RequestSignUpModel.kt @@ -0,0 +1,7 @@ +package com.going.domain.entity.request + +data class RequestSignUpModel( + val name: String, + val intro: String, + val platform: String, +) diff --git a/domain/src/main/kotlin/com/going/domain/repository/AuthRepository.kt b/domain/src/main/kotlin/com/going/domain/repository/AuthRepository.kt new file mode 100644 index 00000000..1f4ebdd7 --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/repository/AuthRepository.kt @@ -0,0 +1,17 @@ +package com.going.domain.repository + +import com.going.domain.entity.request.RequestSignInModel +import com.going.domain.entity.request.RequestSignUpModel +import com.going.domain.entity.response.AuthTokenModel + +interface AuthRepository { + suspend fun postSignIn( + Authorization: String, + data: RequestSignInModel, + ): Result + + suspend fun postSignUp( + Authorization: String, + data: RequestSignUpModel, + ): Result +} diff --git a/domain/src/main/kotlin/com/going/domain/repository/LoginRepository.kt b/domain/src/main/kotlin/com/going/domain/repository/LoginRepository.kt deleted file mode 100644 index 8b70be28..00000000 --- a/domain/src/main/kotlin/com/going/domain/repository/LoginRepository.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.going.domain.repository - -import com.going.domain.entity.response.AuthTokenModel - -interface LoginRepository { - suspend fun postSignin( - Authorization: String, - platform: String, - ): Result -} diff --git a/domain/src/main/kotlin/com/going/domain/repository/TokenRepository.kt b/domain/src/main/kotlin/com/going/domain/repository/TokenRepository.kt new file mode 100644 index 00000000..dbcbbff1 --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/repository/TokenRepository.kt @@ -0,0 +1,9 @@ +package com.going.domain.repository + +interface TokenRepository { + fun getAccessToken(): String + + fun getRefreshToken(): String + + fun setTokens(accessToken: String, refreshToken: String) +} diff --git a/presentation/src/main/java/com/going/presentation/onboarding/OnboardingProfileSettingViewModel.kt b/presentation/src/main/java/com/going/presentation/onboarding/OnboardingProfileSettingViewModel.kt deleted file mode 100644 index 73c90f54..00000000 --- a/presentation/src/main/java/com/going/presentation/onboarding/OnboardingProfileSettingViewModel.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.going.presentation.onboarding - -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import com.going.domain.entity.NameState -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import java.text.BreakIterator - -class OnboardingProfileSettingViewModel : ViewModel() { - val name = MutableLiveData(String()) - val nowNameLength = MutableLiveData(0) - val info = MutableLiveData(String()) - val nowInfoLength = MutableLiveData(0) - - val isNameAvailable = MutableLiveData(NameState.Empty) - val isProfileAvailable = MutableLiveData(false) - - private val _isMoveScreenAvailable = MutableStateFlow(false) - val isMoveScreenAvailable: StateFlow = _isMoveScreenAvailable - - fun getMaxNameLen() = MAX_NAME_LEN - fun getMaxInfoLen() = MAX_INFO_LEN - - fun checkProfileAvailable() { - nowNameLength.value = getGraphemeLength(name.value) - nowInfoLength.value = getGraphemeLength(info.value) - - isNameAvailable.value = when { - nowNameLength.value == 0 -> NameState.Empty - name.value.isNullOrBlank() -> NameState.Blank - else -> NameState.Success - } - - val isInfoAvailable = getGraphemeLength(info.value) in 1..MAX_INFO_LEN - - isProfileAvailable.value = - (isNameAvailable.value == NameState.Success) && isInfoAvailable - } - - // 이모지 포함 글자 수 세는 함수 - private fun getGraphemeLength(value: String?): Int { - BREAK_ITERATOR.setText(value) - - var count = 0 - while (BREAK_ITERATOR.next() != BreakIterator.DONE) { - count++ - } - - return count - } - - fun setIsMoveScreenAvailable() { - _isMoveScreenAvailable.value = true - } - - companion object { - val BREAK_ITERATOR: BreakIterator = BreakIterator.getCharacterInstance() - - const val MAX_NAME_LEN = 3 - const val MAX_INFO_LEN = 20 - } -} diff --git a/presentation/src/main/java/com/going/presentation/auth/SignInActivity.kt b/presentation/src/main/java/com/going/presentation/onboarding/signin/SignInActivity.kt similarity index 57% rename from presentation/src/main/java/com/going/presentation/auth/SignInActivity.kt rename to presentation/src/main/java/com/going/presentation/onboarding/signin/SignInActivity.kt index a11e9a6f..40de59c6 100644 --- a/presentation/src/main/java/com/going/presentation/auth/SignInActivity.kt +++ b/presentation/src/main/java/com/going/presentation/onboarding/signin/SignInActivity.kt @@ -1,4 +1,4 @@ -package com.going.presentation.auth +package com.going.presentation.onboarding.signin import android.content.Intent import android.net.Uri @@ -8,9 +8,11 @@ import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import com.going.presentation.R import com.going.presentation.databinding.ActivitySigninBinding +import com.going.presentation.onboarding.signup.OnboardingProfileSettingActivity +import com.going.presentation.tendencytest.TendencyTestActivity import com.going.ui.base.BaseActivity -import com.going.ui.extension.UiState import com.going.ui.extension.setOnSingleClickListener +import com.going.ui.extension.toast import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -53,28 +55,43 @@ class SignInActivity : BaseActivity(R.layout.activity_sig } private fun observePostChangeTokenState() { - viewModel.postChangeTokenState.flowWithLifecycle(lifecycle).onEach { tokenState -> - when (tokenState) { - is UiState.Success -> { - // 성공 했을 때 로직 - } + viewModel.postChangeTokenState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + SignInState.SUCCESS -> navigateToMainScreen() + SignInState.SIGN_UP -> navigateToOnboardingScreen() + SignInState.TENDENCY -> navigateToTendencyScreen() + SignInState.FAIL -> toast(getString(R.string.server_error)) + SignInState.LOADING -> {} + } + }.launchIn(lifecycleScope) + } - is UiState.Failure -> { - // 실패 했을 때 로직 - } + private fun navigateToMainScreen() { + // 추후 대시보드 연결시 연결 예정 + Intent(this, OnboardingProfileSettingActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(this) + } + finish() + } - is UiState.Empty -> { - // 여튼 로직 - } + private fun navigateToOnboardingScreen() { + Intent(this, OnboardingProfileSettingActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(this) + } + finish() + } - is UiState.Loading -> { - // 로딩 중 로직 - } - } - }.launchIn(lifecycleScope) + private fun navigateToTendencyScreen() { + Intent(this, TendencyTestActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(this) + } + finish() } - companion object{ + companion object { const val TERMS_URL = "http://www.naver.com" } } diff --git a/presentation/src/main/java/com/going/presentation/onboarding/signin/SignInState.kt b/presentation/src/main/java/com/going/presentation/onboarding/signin/SignInState.kt new file mode 100644 index 00000000..667187fa --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/onboarding/signin/SignInState.kt @@ -0,0 +1,9 @@ +package com.going.presentation.onboarding.signin + +sealed interface SignInState { + object LOADING : SignInState + object SUCCESS : SignInState + object FAIL : SignInState + object SIGN_UP : SignInState + object TENDENCY : SignInState +} diff --git a/presentation/src/main/java/com/going/presentation/auth/SignInViewModel.kt b/presentation/src/main/java/com/going/presentation/onboarding/signin/SignInViewModel.kt similarity index 64% rename from presentation/src/main/java/com/going/presentation/auth/SignInViewModel.kt rename to presentation/src/main/java/com/going/presentation/onboarding/signin/SignInViewModel.kt index b4bcc28f..d4b80f38 100644 --- a/presentation/src/main/java/com/going/presentation/auth/SignInViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/onboarding/signin/SignInViewModel.kt @@ -1,11 +1,12 @@ -package com.going.presentation.auth +package com.going.presentation.onboarding.signin import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.going.domain.entity.response.AuthTokenModel -import com.going.domain.repository.LoginRepository -import com.going.ui.extension.UiState +import com.going.domain.entity.request.RequestSignInModel +import com.going.domain.repository.AuthRepository +import com.going.domain.repository.TokenRepository +import com.going.presentation.util.toErrorCode import com.kakao.sdk.auth.model.OAuthToken import com.kakao.sdk.common.model.ClientError import com.kakao.sdk.common.model.ClientErrorCause @@ -14,15 +15,15 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch -import timber.log.Timber import javax.inject.Inject @HiltViewModel class SignInViewModel @Inject constructor( - private val loginRepository: LoginRepository, + private val authRepository: AuthRepository, + private val tokenRepository: TokenRepository, ) : ViewModel() { - private val _postChangeTokenState = MutableStateFlow>(UiState.Empty) - val postChangeTokenState: StateFlow> = _postChangeTokenState + private val _postChangeTokenState = MutableStateFlow(SignInState.LOADING) + val postChangeTokenState: StateFlow = _postChangeTokenState private val _isAppLoginAvailable = MutableStateFlow(true) val isAppLoginAvailable: StateFlow = _isAppLoginAvailable @@ -65,28 +66,30 @@ class SignInViewModel @Inject constructor( // 서버통신 - 카카오 토큰 보내서 서비스 토큰 받아오기 - 서버와 협의 후 수정예정 private fun changeTokenFromServer( accessToken: String, - social: String = KAKAO, + platform: String = KAKAO, ) { - _postChangeTokenState.value = UiState.Loading + _postChangeTokenState.value = SignInState.LOADING viewModelScope.launch { - // 통신 로직 - loginRepository.postSignin(accessToken, social).onSuccess { - // 성공시 서버에서 준 정보를 넣는 예시 코드 - Timber.e("성공고오고오고공") - _postChangeTokenState.value = UiState.Success( - AuthTokenModel( - accessToken = "testAccessToekn", - refreshToken = "testRefreshToekn", - ), - ) - }.onFailure { err -> - Timber.e("실패패패패패패") + authRepository.postSignIn(accessToken, RequestSignInModel(platform)).onSuccess { + tokenRepository.setTokens(it.accessToken, it.refreshToken) + + _postChangeTokenState.value = SignInState.SUCCESS + }.onFailure { + val errorCode = toErrorCode(it) + + _postChangeTokenState.value = when (errorCode) { + SIGN_UP -> SignInState.SIGN_UP + TENDENCY -> SignInState.TENDENCY + else -> SignInState.FAIL + } } } } companion object { const val KAKAO = "kakao" + const val SIGN_UP = "e4041" + const val TENDENCY = "e4045" } } diff --git a/presentation/src/main/java/com/going/presentation/onboarding/OnboardingProfileSettingActivity.kt b/presentation/src/main/java/com/going/presentation/onboarding/signup/OnboardingProfileSettingActivity.kt similarity index 76% rename from presentation/src/main/java/com/going/presentation/onboarding/OnboardingProfileSettingActivity.kt rename to presentation/src/main/java/com/going/presentation/onboarding/signup/OnboardingProfileSettingActivity.kt index 02b2b608..3174a88c 100644 --- a/presentation/src/main/java/com/going/presentation/onboarding/OnboardingProfileSettingActivity.kt +++ b/presentation/src/main/java/com/going/presentation/onboarding/signup/OnboardingProfileSettingActivity.kt @@ -1,5 +1,6 @@ -package com.going.presentation.onboarding +package com.going.presentation.onboarding.signup +import android.content.Intent import android.os.Bundle import android.view.inputmethod.EditorInfo import android.widget.TextView @@ -10,10 +11,16 @@ import androidx.lifecycle.lifecycleScope import com.going.domain.entity.NameState import com.going.presentation.R import com.going.presentation.databinding.ActivityOnboardingProfileSettingBinding +import com.going.presentation.onboarding.splash.SplashActivity +import com.going.presentation.tendencytest.TendencyTestActivity import com.going.ui.base.BaseActivity +import com.going.ui.extension.setOnSingleClickListener +import com.going.ui.extension.toast +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +@AndroidEntryPoint class OnboardingProfileSettingActivity : BaseActivity(R.layout.activity_onboarding_profile_setting) { private val viewModel by viewModels() @@ -24,9 +31,10 @@ class OnboardingProfileSettingActivity : initBindingViewModel() initOnLineInfoEditorActionListener() initSetOnFocusChangeListener() + initSignUpBtnClickListener() observeIsNameAvailable() - observeIsProfileAvailable() observeTextLength() + observeIsSignUpState() } private fun initBindingViewModel() { @@ -70,6 +78,12 @@ class OnboardingProfileSettingActivity : } } + private fun initSignUpBtnClickListener() { + binding.btnOnboardingProfileSettingFinish.setOnSingleClickListener { + viewModel.startSignUp() + } + } + private fun observeIsNameAvailable() { viewModel.isNameAvailable.observe(this) { state -> setColors( @@ -107,12 +121,6 @@ class OnboardingProfileSettingActivity : counter.setTextColor(getColor(color)) } - private fun observeIsProfileAvailable() { - viewModel.isMoveScreenAvailable.flowWithLifecycle(lifecycle).onEach { isEnd -> - if (isEnd) moveSplash() - }.launchIn(lifecycleScope) - } - // 커스텀 글자수 제한 함수 private fun observeTextLength() { viewModel.nowNameLength.observe(this) { length -> @@ -138,7 +146,30 @@ class OnboardingProfileSettingActivity : } } - private fun moveSplash() { - // 스플래시로 이동 + private fun observeIsSignUpState() { + viewModel.isSignUpState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + is SignUpState.SUCCESS -> navigateToTendencyTestScreen() + is SignUpState.FAIL -> toast(getString(R.string.server_error)) + is SignUpState.LOG_IN -> navigateToSplashScreen() + is SignUpState.LOADING -> {} + } + }.launchIn(lifecycleScope) + } + + private fun navigateToSplashScreen() { + Intent(this, SplashActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(this) + } + finish() + } + + private fun navigateToTendencyTestScreen() { + Intent(this, TendencyTestActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(this) + } + finish() } } diff --git a/presentation/src/main/java/com/going/presentation/onboarding/signup/OnboardingProfileSettingViewModel.kt b/presentation/src/main/java/com/going/presentation/onboarding/signup/OnboardingProfileSettingViewModel.kt new file mode 100644 index 00000000..37fb9b51 --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/onboarding/signup/OnboardingProfileSettingViewModel.kt @@ -0,0 +1,105 @@ +package com.going.presentation.onboarding.signup + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.going.domain.entity.NameState +import com.going.domain.entity.request.RequestSignUpModel +import com.going.domain.repository.AuthRepository +import com.kakao.sdk.auth.AuthApiClient +import com.kakao.sdk.auth.TokenManagerProvider +import com.kakao.sdk.user.UserApiClient +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import java.text.BreakIterator +import javax.inject.Inject + +@HiltViewModel +class OnboardingProfileSettingViewModel @Inject constructor( + private val authRepository: AuthRepository, +) : ViewModel() { + val name = MutableStateFlow("") + val nowNameLength = MutableLiveData(0) + val info = MutableStateFlow("") + val nowInfoLength = MutableLiveData(0) + + val isNameAvailable = MutableLiveData(NameState.Empty) + val isProfileAvailable = MutableLiveData(false) + + private val _isSignUpState = MutableStateFlow(SignUpState.LOADING) + val isSignUpState: StateFlow = _isSignUpState + + fun getMaxNameLen() = MAX_NAME_LEN + fun getMaxInfoLen() = MAX_INFO_LEN + + fun checkProfileAvailable() { + nowNameLength.value = getGraphemeLength(name.value) + nowInfoLength.value = getGraphemeLength(info.value) + + isNameAvailable.value = when { + nowNameLength.value == 0 -> NameState.Empty + name.value.isNullOrBlank() -> NameState.Blank + else -> NameState.Success + } + + val isInfoAvailable = getGraphemeLength(info.value) in 1..MAX_INFO_LEN + + isProfileAvailable.value = + (isNameAvailable.value == NameState.Success) && isInfoAvailable + } + + // 이모지 포함 글자 수 세는 함수 + private fun getGraphemeLength(value: String?): Int { + BREAK_ITERATOR.setText(value) + + var count = 0 + while (BREAK_ITERATOR.next() != BreakIterator.DONE) { + count++ + } + + return count + } + + fun startSignUp() { + _isSignUpState.value = SignUpState.LOADING + + if (AuthApiClient.instance.hasToken()) { + UserApiClient.instance.accessTokenInfo { _, error -> + if (error == null) { + val kakaoAccessToken = + TokenManagerProvider.instance.manager.getToken()?.accessToken + signUpWithServer(kakaoAccessToken.toString()) + } else { + _isSignUpState.value = SignUpState.LOG_IN + } + } + } else { + _isSignUpState.value = SignUpState.LOG_IN + } + } + + private fun signUpWithServer(kakaoAccessToken: String) { + viewModelScope.launch { + authRepository.postSignUp( + kakaoAccessToken, + RequestSignUpModel(name.value, info.value, KAKAO), + ).onSuccess { + _isSignUpState.value = SignUpState.SUCCESS + }.onFailure { + _isSignUpState.value = SignUpState.FAIL + } + } + } + + companion object { + val BREAK_ITERATOR: BreakIterator = BreakIterator.getCharacterInstance() + + const val KAKAO = "kakao" + const val MAX_NAME_LEN = 3 + const val MAX_INFO_LEN = 20 + const val SIGN_UP = "e4041" + const val TENDENCY = "e4045" + } +} diff --git a/presentation/src/main/java/com/going/presentation/onboarding/signup/SignUpState.kt b/presentation/src/main/java/com/going/presentation/onboarding/signup/SignUpState.kt new file mode 100644 index 00000000..36394c87 --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/onboarding/signup/SignUpState.kt @@ -0,0 +1,8 @@ +package com.going.presentation.onboarding.signup + +sealed interface SignUpState { + object LOADING : SignUpState + object SUCCESS : SignUpState + object FAIL : SignUpState + object LOG_IN : SignUpState +} diff --git a/presentation/src/main/java/com/going/presentation/splash/NetworkManager.kt b/presentation/src/main/java/com/going/presentation/onboarding/splash/NetworkManager.kt similarity index 93% rename from presentation/src/main/java/com/going/presentation/splash/NetworkManager.kt rename to presentation/src/main/java/com/going/presentation/onboarding/splash/NetworkManager.kt index 8474f875..06cde7dc 100644 --- a/presentation/src/main/java/com/going/presentation/splash/NetworkManager.kt +++ b/presentation/src/main/java/com/going/presentation/onboarding/splash/NetworkManager.kt @@ -1,4 +1,4 @@ -package com.going.presentation.splash +package com.going.presentation.onboarding.splash import android.content.Context import android.net.ConnectivityManager diff --git a/presentation/src/main/java/com/going/presentation/splash/SplashActivity.kt b/presentation/src/main/java/com/going/presentation/onboarding/splash/SplashActivity.kt similarity index 74% rename from presentation/src/main/java/com/going/presentation/splash/SplashActivity.kt rename to presentation/src/main/java/com/going/presentation/onboarding/splash/SplashActivity.kt index a6758d43..31f83c8d 100644 --- a/presentation/src/main/java/com/going/presentation/splash/SplashActivity.kt +++ b/presentation/src/main/java/com/going/presentation/onboarding/splash/SplashActivity.kt @@ -1,16 +1,21 @@ -package com.going.presentation.splash +package com.going.presentation.onboarding.splash import android.app.AlertDialog import android.content.Intent import android.os.Bundle import android.os.Handler import android.os.Looper +import androidx.activity.viewModels import com.going.presentation.R -import com.going.presentation.auth.SignInActivity import com.going.presentation.databinding.ActivitySplashBinding +import com.going.presentation.onboarding.signin.SignInActivity +import com.going.presentation.onboarding.signup.OnboardingProfileSettingActivity import com.going.ui.base.BaseActivity +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class SplashActivity : BaseActivity(R.layout.activity_splash) { + private val viewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -27,8 +32,7 @@ class SplashActivity : BaseActivity(R.layout.activity_spl private fun initSplash() { Handler(Looper.getMainLooper()).postDelayed({ - navigateToSignInScreen() - if (false) { // 자동 로그인 판정으로 변경 예정 + if (viewModel.getHasAccessToken()) { navigateToMainScreen() } else { navigateToSignInScreen() @@ -51,6 +55,10 @@ class SplashActivity : BaseActivity(R.layout.activity_spl private fun navigateToMainScreen() { // Main이 나오면 구현 예정 + Intent(this, OnboardingProfileSettingActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(this) + } finish() } diff --git a/presentation/src/main/java/com/going/presentation/onboarding/splash/SplashViewModel.kt b/presentation/src/main/java/com/going/presentation/onboarding/splash/SplashViewModel.kt new file mode 100644 index 00000000..5ea3c164 --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/onboarding/splash/SplashViewModel.kt @@ -0,0 +1,13 @@ +package com.going.presentation.onboarding.splash + +import androidx.lifecycle.ViewModel +import com.going.domain.repository.TokenRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class SplashViewModel @Inject constructor( + private val tokenRepository: TokenRepository, +) : ViewModel() { + fun getHasAccessToken(): Boolean = tokenRepository.getAccessToken() != "" +} diff --git a/presentation/src/main/java/com/going/presentation/util/JsonExt.kt b/presentation/src/main/java/com/going/presentation/util/JsonExt.kt new file mode 100644 index 00000000..9aea58f3 --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/util/JsonExt.kt @@ -0,0 +1,12 @@ +package com.going.presentation.util + +import org.json.JSONObject +import retrofit2.HttpException + +fun toErrorCode(throwable: Throwable): String = if (throwable is HttpException) { + val jsonTemp = throwable.response()?.errorBody()?.byteString().toString() + val json = jsonTemp.slice(6 until jsonTemp.length) + JSONObject(json).getString("code") +} else { + "NOT_HTTP" +} diff --git a/presentation/src/main/res/layout/activity_onboarding_profile_setting.xml b/presentation/src/main/res/layout/activity_onboarding_profile_setting.xml index 6e2ef9f8..59aad5ba 100644 --- a/presentation/src/main/res/layout/activity_onboarding_profile_setting.xml +++ b/presentation/src/main/res/layout/activity_onboarding_profile_setting.xml @@ -10,7 +10,7 @@ + type="com.going.presentation.onboarding.signup.OnboardingProfileSettingViewModel" />