Skip to content

Commit

Permalink
Implement profile create.
Browse files Browse the repository at this point in the history
  • Loading branch information
bywindow committed Aug 3, 2024
1 parent c6329f7 commit 9e80ec4
Show file tree
Hide file tree
Showing 44 changed files with 920 additions and 26 deletions.
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

0 comments on commit 9e80ec4

Please sign in to comment.