Skip to content

Commit

Permalink
refactor #11 - SignInScreen MVI 리팩토링
Browse files Browse the repository at this point in the history
  • Loading branch information
sayyyho committed Dec 17, 2024
1 parent 4276b73 commit c7c16cf
Show file tree
Hide file tree
Showing 19 changed files with 576 additions and 230 deletions.
33 changes: 33 additions & 0 deletions app/src/main/java/org/sopt/and/core/utils/PreferenceUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.sopt.and.core.utils

import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject


class PreferenceUtil @Inject constructor(
@ApplicationContext private val context: Context
) {
private val sharedPreferences =
context.getSharedPreferences("wavve_prefs", Context.MODE_PRIVATE)

fun saveUserToken(token: String) {
sharedPreferences.edit().putString(USER_TOKEN, token).apply()
}

fun getUserToken(): String? {
return sharedPreferences.getString(USER_TOKEN, null)
}

fun clearUserToken() {
sharedPreferences.edit().remove(USER_TOKEN).apply()
}

fun clearAll() {
sharedPreferences.edit().clear().apply()
}

companion object {
private const val USER_TOKEN = "user_token"
}
}
33 changes: 33 additions & 0 deletions app/src/main/java/org/sopt/and/core/utils/SnackBarUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.sopt.and.core.utils

import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult

object SnackBarUtils {
private lateinit var snackBarHostState: SnackbarHostState

fun init(snackBarHostState: SnackbarHostState) {
SnackBarUtils.snackBarHostState = snackBarHostState
}

suspend fun showSnackBar(
message: String,
actionLabel: String? = null,
onActionClick: (() -> Unit)? = null,
duration: SnackbarDuration = SnackbarDuration.Short,
) {
if (this::snackBarHostState.isInitialized) {
val result = snackBarHostState.showSnackbar(
message = message,
actionLabel = actionLabel,
duration = duration
)
if (result == SnackbarResult.ActionPerformed) {
onActionClick?.invoke()
}
} else {
throw UninitializedPropertyAccessException("SnackBarHostState가 초기화 되지 않았습니다. init()을 먼저 호출하세요")
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.sopt.and.core.util
package org.sopt.and.core.utils

sealed class UiState<out T> {
object Loading : UiState<Nothing>()
Expand Down
20 changes: 20 additions & 0 deletions app/src/main/java/org/sopt/and/data/di/PreferenceModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.sopt.and.data.di

import android.content.Context
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import org.sopt.and.core.utils.PreferenceUtil
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object PreferenceModule {
@Provides
@Singleton
fun providePreferenceUtil(@ApplicationContext context: Context): PreferenceUtil {
return PreferenceUtil(context)
}
}
39 changes: 39 additions & 0 deletions app/src/main/java/org/sopt/and/data/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.sopt.and.data.di

import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.sopt.and.data.datasource.MyHobbyDataSource
import org.sopt.and.data.datasource.SignInDataSource
import org.sopt.and.data.datasource.SignUpDataSource
import org.sopt.and.data.repositoryimpl.MyHobbyRepositoryImpl
import org.sopt.and.data.repositoryimpl.SignInRepositoryImpl
import org.sopt.and.data.repositoryimpl.SignUpRepositoryImpl
import org.sopt.and.domain.repository.MyHobbyRepository
import org.sopt.and.domain.repository.SignUpRepository
import org.sopt.and.domain.repository.SignInRepository
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object RepositoryModule {

@Provides
@Singleton
fun provideUserRegisterRepository(userService: SignUpDataSource): SignUpRepository {
return SignUpRepositoryImpl(userService)
}

@Provides
@Singleton
fun provideUserLoginRepository(userService: SignInDataSource): SignInRepository {
return SignInRepositoryImpl(userService)
}

@Provides
@Singleton
fun provideGetMyHobbyRepository(userService: MyHobbyDataSource): MyHobbyRepository {
return MyHobbyRepositoryImpl(userService)
}
}
45 changes: 45 additions & 0 deletions app/src/main/java/org/sopt/and/data/di/RetrofitModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.sopt.and.data.di

import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import org.sopt.and.BuildConfig.BASE_URL
import retrofit2.Retrofit
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object RetrofitModule {

@Provides
@Singleton
fun provideLoggingInterceptor(): HttpLoggingInterceptor {
return HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
}

@Provides
@Singleton
fun provideOkHttpClient(loggingInterceptor: HttpLoggingInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()
}

@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.build()
}
}
36 changes: 36 additions & 0 deletions app/src/main/java/org/sopt/and/data/di/UseCaseModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.sopt.and.data.di

import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.sopt.and.domain.repository.MyHobbyRepository
import org.sopt.and.domain.repository.SignUpRepository
import org.sopt.and.domain.repository.SignInRepository
import org.sopt.and.domain.usecase.MyHobbyUseCase
import org.sopt.and.domain.usecase.SignInUseCase
import org.sopt.and.domain.usecase.SignUpUseCase
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object UseCaseModule {

@Provides
@Singleton
fun provideRegisterUserUseCase(userRepository: SignUpRepository): SignUpUseCase {
return SignUpUseCase(userRepository)
}

@Provides
@Singleton
fun provideLoginUserUseCase(userRepository: SignInRepository): SignInUseCase {
return SignInUseCase(userRepository)
}

@Provides
@Singleton
fun provideGetMyHobbyUseCase(userRepository: MyHobbyRepository): MyHobbyUseCase {
return MyHobbyUseCase(userRepository)
}
}
9 changes: 9 additions & 0 deletions app/src/main/java/org/sopt/and/domain/entity/BaseResult.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.sopt.and.domain.entity

sealed class BaseResult<out T> {
data class Success<out T>(val data: T) : BaseResult<T>()
data class Error(
val message: String,
val errorCode: String? = null
) : BaseResult<Nothing>()
}
7 changes: 7 additions & 0 deletions app/src/main/java/org/sopt/and/domain/entity/UserData.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.sopt.and.domain.entity

data class UserData(
val username: String,
val password: String,
val hobby: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.sopt.and.domain.entity

data class UserLoginResult(
val token: String
)
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
package org.sopt.and.domain.repository

import org.sopt.and.data.datasource.SignInDataSource
import org.sopt.and.data.repositoryimpl.SignInRepositoryImpl
import org.sopt.and.data.service.ServicePool
import org.sopt.and.domain.model.SignInInformationEntity
import org.sopt.and.domain.model.SignInResponseEntity

interface SignInRepository {
suspend fun signIn(request: SignInInformationEntity): Result<SignInResponseEntity>
import org.sopt.and.domain.entity.BaseResult
import org.sopt.and.domain.entity.UserLoginResult


companion object {
fun create(): SignInRepositoryImpl {
return SignInRepositoryImpl(
SignInDataSource(
ServicePool.userService
)
)
}
}
interface SignInRepository {
suspend fun loginUser(user: org.sopt.and.domain.entity.UserData): BaseResult<UserLoginResult>
}
15 changes: 9 additions & 6 deletions app/src/main/java/org/sopt/and/domain/usecase/SignInUseCase.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package org.sopt.and.domain.usecase

import org.sopt.and.domain.model.SignInInformationEntity
import org.sopt.and.domain.model.SignInResponseEntity
import org.sopt.and.domain.entity.BaseResult
import org.sopt.and.domain.entity.UserData
import org.sopt.and.domain.entity.UserLoginResult
import org.sopt.and.domain.repository.SignInRepository
import javax.inject.Inject

class SignInUseCase(
private val signInRepository: SignInRepository
class SignInUseCase @Inject constructor(
private val userLoginRepository: SignInRepository
) {
suspend operator fun invoke(request: SignInInformationEntity): Result<SignInResponseEntity> =
signInRepository.signIn(request = request)
suspend operator fun invoke(user: UserData): BaseResult<UserLoginResult> {
return userLoginRepository.loginUser(user)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.sopt.and.presentation.auth.signin

import org.sopt.and.presentation.util.UiEffect
import org.sopt.and.presentation.util.UiEvent
import org.sopt.and.presentation.util.UiState

class SignInContract {
data class SignInUiState(
val username: String = "",
val password: String = "",
val isLoading: Boolean = false,
val errorMessage: String? = null
) : UiState

sealed class SignInUiEvent : UiEvent {
data class UpdateUserName(val username: String) : SignInUiEvent()
data class UpdatePassword(val password: String) : SignInUiEvent()
data object SignInFormSubmit : SignInUiEvent()
data object NavigateUp : SignInUiEvent()
}

sealed class SignInUiEffect : UiEffect {
data object ShowSuccessSnackBar : SignInUiEffect()
data class ShowErrorSnackBar(val message: String) : SignInUiEffect()
data object NavigateToSignUp : SignInUiEffect()
data object NavigateToMy : SignInUiEffect()
data object NavigateUp : SignInUiEffect()
}
}
Loading

0 comments on commit c7c16cf

Please sign in to comment.