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

Implement profile create. #16

Merged
merged 1 commit into from
Aug 5, 2024
Merged
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
6 changes: 0 additions & 6 deletions .idea/kotlinc.xml

This file was deleted.

1 change: 1 addition & 0 deletions core/data/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ dependencies {

implementation(libs.bundles.test)
implementation(libs.bundles.coroutines)
implementation(libs.retrofit)

ksp(libs.hilt.compiler)
implementation(libs.hilt.android)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.goalpanzi.mission_mate.core.data.di

import com.goalpanzi.mission_mate.core.data.repository.LoginRepositoryImpl
import com.goalpanzi.mission_mate.core.data.repository.ProfileRepositoryImpl
import com.goalpanzi.mission_mate.core.domain.repository.LoginRepository
import com.goalpanzi.mission_mate.core.domain.repository.ProfileRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
Expand All @@ -13,4 +15,7 @@ internal abstract class DataModule {

@Binds
abstract fun bindLoginRepository(impl: LoginRepositoryImpl): LoginRepository

@Binds
abstract fun bindProfileRepository(impl: ProfileRepositoryImpl): ProfileRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.goalpanzi.mission_mate.core.data.repository

import com.goalpanzi.mission_mate.core.domain.repository.ProfileRepository
import com.goalpanzi.mission_mate.core.network.service.ProfileService
import com.luckyoct.core.model.base.NetworkResult
import com.luckyoct.core.model.request.SaveProfileRequest
import javax.inject.Inject

class ProfileRepositoryImpl @Inject constructor(
private val profileService: ProfileService
): ProfileRepository {
override suspend fun saveProfile(nickname: String, index: Int): NetworkResult<Unit> = handleResult {
val request = SaveProfileRequest.createRequest(nickname, index)
profileService.saveProfile(request)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.goalpanzi.mission_mate.core.datastore.datasource

import android.util.Log
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
Expand Down
7 changes: 7 additions & 0 deletions core/designsystem/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,11 @@
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>

<color name="rabbit_color">#FFFFE4E4</color>
<color name="cat_color">#FFBFD7FF</color>
<color name="dog_color">#FFFFE59A</color>
<color name="panda_color">#FFC2E792</color>
<color name="bear_color">#FFF7D8B3</color>
<color name="bird_color">#FFBCE7FF</color>
</resources>
2 changes: 2 additions & 0 deletions core/domain/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,6 @@ dependencies {
implementation(libs.hilt.android)

implementation(project(":core:model"))
implementation(project(":core:datastore"))
implementation(project(":core:network"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.goalpanzi.mission_mate.core.domain.di

import android.content.Context
import android.content.res.TypedArray
import androidx.annotation.ArrayRes
import androidx.annotation.StringRes
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class ResourceProvider @Inject constructor(
@ApplicationContext private val context: Context
) {
fun getString(@StringRes stringResId: Int): String {
return context.getString(stringResId)
}

fun getIntArray(@ArrayRes arrayResId: Int): Array<Int> {
return context.resources.getIntArray(arrayResId).toTypedArray()
}

fun getDrawableArray(@ArrayRes arrayResId: Int): TypedArray {
return context.resources.obtainTypedArray(arrayResId)
}

fun getStringArray(@ArrayRes arrayResId: Int): Array<String> {
return context.resources.getStringArray(arrayResId)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.goalpanzi.mission_mate.core.domain.repository

import com.goalpanzi.mission_mate.core.network.ResultHandler
import com.luckyoct.core.model.base.NetworkResult

interface ProfileRepository: ResultHandler {
suspend fun saveProfile(nickname: String, index: Int): NetworkResult<Unit>
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
package com.goalpanzi.mission_mate.core.domain.usecase

import com.goalpanzi.mission_mate.core.datastore.datasource.AuthDataSource
import com.goalpanzi.mission_mate.core.domain.repository.LoginRepository
import com.luckyoct.core.model.GoogleLogin
import kotlinx.coroutines.flow.first
import javax.inject.Inject

class LoginUseCase @Inject constructor(
private val loginRepository: LoginRepository
private val loginRepository: LoginRepository,
private val authDataSource: AuthDataSource
) {

suspend fun requestGoogleLogin(token: String, email: String): GoogleLogin = loginRepository.requestGoogleLogin(token, email)
suspend fun requestGoogleLogin(token: String, email: String): GoogleLogin {
val response = loginRepository.requestGoogleLogin(token, email)
authDataSource.setAccessToken(response.accessToken).first()
authDataSource.setRefreshToken(response.refreshToken).first()
return response
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.goalpanzi.mission_mate.core.domain.usecase

import com.goalpanzi.mission_mate.core.domain.repository.ProfileRepository
import javax.inject.Inject

class ProfileUseCase @Inject constructor(
private val profileRepository: ProfileRepository
) {
suspend fun saveProfile(nickname: String, index: Int) = profileRepository.saveProfile(nickname, index)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.luckyoct.core.model.base

sealed interface NetworkResult<out T> {
data class Success<out T>(val data: T) : NetworkResult<T>
data class Error(val code: Int? = null, val message: String? = null) : NetworkResult<Nothing>
data class Exception(val error: Throwable) : NetworkResult<Nothing>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.luckyoct.core.model.request

import kotlinx.serialization.Serializable

enum class CharacterType {
RABBIT, CAT, DOG, PANDA, BEAR, BIRD
}

@Serializable
data class SaveProfileRequest(
val nickname: String,
val characterType: String,
) {
companion object {
fun createRequest(nickname: String, index: Int) = SaveProfileRequest(
nickname = nickname,
characterType = CharacterType.entries[index].name.uppercase()
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ sealed interface RouteModel {

@Serializable
data object Onboarding : RouteModel

@Serializable
sealed interface Profile: RouteModel {
@Serializable
data object Create : Profile
@Serializable
data object Change : Profile
}
}

sealed interface OnboardingRouteModel {
Expand Down
1 change: 1 addition & 0 deletions core/network/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ dependencies {
implementation(libs.hilt.android)

implementation(project(":core:model"))
implementation(project(":core:datastore"))
}

fun getMissionMateBaseUrl(): String {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.goalpanzi.mission_mate.core.network

import com.luckyoct.core.model.base.NetworkResult
import retrofit2.HttpException
import retrofit2.Response

interface ResultHandler {

suspend fun <T : Any> handleResult(execute: suspend () -> Response<T>): NetworkResult<T> {
return try {
val response = execute()
if (response.isSuccessful) {
val body = response.body()
if (body != null) {
NetworkResult.Success(body)
} else {
NetworkResult.Error(response.code(), "Response body is null")
}
} else {
NetworkResult.Error(response.code(), response.errorBody()?.string())
}
} catch (e: HttpException) {
NetworkResult.Error(e.code(), e.message())
} catch (e: Throwable) {
NetworkResult.Exception(e)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.goalpanzi.mission_mate.core.network

import com.goalpanzi.mission_mate.core.datastore.datasource.AuthDataSource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import okhttp3.Interceptor
import okhttp3.Response
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class TokenInterceptor @Inject constructor(
private val authDataSource: AuthDataSource
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val newRequest = chain.request().newBuilder().apply {
runBlocking {
val token = authDataSource.getAccessToken().first()
token?.let {
addHeader("Authorization", "Bearer $it")
}
}
}

val response = chain.proceed(newRequest.build())
if (response.code == 200) {
val newAccessToken: String = response.header("Authorization", null) ?: return response
CoroutineScope(Dispatchers.IO).launch {
val existedAccessToken = authDataSource.getAccessToken().first()
if (existedAccessToken != newAccessToken) {
authDataSource.setAccessToken(newAccessToken)
}
}
}
return response
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.goalpanzi.mission_mate.core.network.di

import com.goalpanzi.mission_mate.core.network.BuildConfig
import com.goalpanzi.mission_mate.core.network.TokenInterceptor
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
Expand All @@ -31,7 +33,8 @@ internal object NetworkModule {
@Provides
@Singleton
fun provideOkhttpClient(
httpLoggingInterceptor: HttpLoggingInterceptor
httpLoggingInterceptor: HttpLoggingInterceptor,
tokenInterceptor: TokenInterceptor
): OkHttpClient {
// TLS 대응
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
Expand All @@ -41,6 +44,7 @@ internal object NetworkModule {
sslContext.init(null, arrayOf(trustManager), java.security.SecureRandom())

return OkHttpClient.Builder()
.addInterceptor(tokenInterceptor)
.sslSocketFactory(sslContext.socketFactory, trustManager)
.addInterceptor(httpLoggingInterceptor)
.build()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.goalpanzi.mission_mate.core.network.di

import com.goalpanzi.mission_mate.core.network.service.LoginService
import com.goalpanzi.mission_mate.core.network.service.ProfileService
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -17,4 +18,10 @@ object ServiceModule {
fun provideLoginService(retrofit: Retrofit): LoginService {
return retrofit.create(LoginService::class.java)
}

@Provides
@Singleton
fun provideProfileService(retrofit: Retrofit): ProfileService {
return retrofit.create(ProfileService::class.java)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.goalpanzi.mission_mate.core.network.service

import com.luckyoct.core.model.request.SaveProfileRequest
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.PATCH

interface ProfileService {
@PATCH("/api/member/profile")
suspend fun saveProfile(
@Body request: SaveProfileRequest
): Response<Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.goalpanzi.mission_mate.feature.login

sealed interface LoginEvent {
data object Error : LoginEvent
data class Success(val isAlreadyMember: Boolean) : LoginEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ fun NavController.navigateToLogin() {
}

fun NavGraphBuilder.loginNavGraph(
onBackClick: () -> Unit
onLoginSuccess: (isProfileSet: Boolean) -> Unit
) {
composable<RouteModel.Login> {
LoginRoute(
onBackClick = onBackClick
onLoginSuccess = onLoginSuccess
)
}
}
Loading
Loading