diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4f12f8b..bc7a74b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,15 +13,6 @@ android { versionName = "1.0" } viewBinding.enable = true - buildTypes { - getByName("release") { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } - } } dependencies { @@ -30,6 +21,6 @@ dependencies { implementation(projects.core.database) implementation(projects.core.common) implementation(projects.core.data) - + implementation(projects.core.network) implementation(projects.feature.main) } \ No newline at end of file diff --git a/build-logic/convention/src/main/java/DataPlugin.kt b/build-logic/convention/src/main/java/DataPlugin.kt index b831d15..5363999 100644 --- a/build-logic/convention/src/main/java/DataPlugin.kt +++ b/build-logic/convention/src/main/java/DataPlugin.kt @@ -16,6 +16,7 @@ internal class DataPlugin : Plugin { dependencies { "implementation"(project(":core:model")) "implementation"(libs.findBundle("coroutine").get()) + "implementation"(libs.findLibrary("androidx-paging").get()) } } } diff --git a/build-logic/convention/src/main/java/org/sopt/convention/KotlinAndroid.kt b/build-logic/convention/src/main/java/org/sopt/convention/KotlinAndroid.kt index 70f859b..68ff2a7 100644 --- a/build-logic/convention/src/main/java/org/sopt/convention/KotlinAndroid.kt +++ b/build-logic/convention/src/main/java/org/sopt/convention/KotlinAndroid.kt @@ -1,6 +1,7 @@ package org.sopt.convention import com.android.build.api.dsl.CommonExtension +import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties import org.gradle.api.Project import org.gradle.api.plugins.ExtensionAware import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions @@ -15,6 +16,17 @@ internal fun Project.configureKotlinAndroid( minSdk = Const.minSdk vectorDrawables.useSupportLibrary = true + + buildConfigField( + type = "String", + name = "AUTH_BASE_URL", + gradleLocalProperties(rootDir, providers).getProperty("auth.base.url") + ) + buildConfigField( + type = "String", + name = "REQRES_BASE_URL", + gradleLocalProperties(rootDir, providers).getProperty("reqres.base.url") + ) } compileOptions { diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts index 3a4b9c9..407c95f 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -12,5 +12,8 @@ dependencies { implementation(projects.core.datastore) implementation(projects.core.database) implementation(projects.core.common) + implementation(projects.core.network) implementation(libs.retrofit.kotlin.serialization) + implementation(libs.kotlinx.serialization.json) + implementation(libs.androidx.paging) } \ No newline at end of file diff --git a/core/data/src/main/java/org/sopt/data/di/DataModule.kt b/core/data/src/main/java/org/sopt/data/di/DataModule.kt index cf4e2e5..8e5dc7f 100644 --- a/core/data/src/main/java/org/sopt/data/di/DataModule.kt +++ b/core/data/src/main/java/org/sopt/data/di/DataModule.kt @@ -4,20 +4,27 @@ import dagger.Binds import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import org.sopt.data.repository.AuthRepositoryImpl import org.sopt.data.repository.FriendRepository import org.sopt.data.repository.UserDataRepositoryImpl +import org.sopt.data.repository.UserRepositoryImpl +import org.sopt.domain.repo.AuthRepository import org.sopt.domain.repo.SoptRepository import org.sopt.domain.repo.UserDataRepository -import javax.inject.Singleton +import org.sopt.domain.repo.UserRepository @Module @InstallIn(SingletonComponent::class) abstract class DataModule { - @Singleton @Binds abstract fun bindsFriendRepo(soptRepository: FriendRepository): SoptRepository - @Singleton @Binds abstract fun bindsUserRepo(userDataRepository: UserDataRepositoryImpl): UserDataRepository + + @Binds + abstract fun bindsMemberRepo(userRepository: UserRepositoryImpl): UserRepository + + @Binds + abstract fun authRepo(authRepository: AuthRepositoryImpl): AuthRepository } \ No newline at end of file diff --git a/core/data/src/main/java/org/sopt/data/paging/UserPagingSource.kt b/core/data/src/main/java/org/sopt/data/paging/UserPagingSource.kt new file mode 100644 index 0000000..1c0bfcb --- /dev/null +++ b/core/data/src/main/java/org/sopt/data/paging/UserPagingSource.kt @@ -0,0 +1,39 @@ +package org.sopt.data.paging + +import androidx.paging.PagingSource +import androidx.paging.PagingState +import org.sopt.model.ReqresUser +import org.sopt.network.api.ReqresApi +import org.sopt.network.model.response.toReqresUser +import retrofit2.HttpException +import java.io.IOException +import javax.inject.Inject + +class UserPagingSource @Inject constructor( + private val reqresApi: ReqresApi, +) : PagingSource() { + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition + } + + override suspend fun load(params: LoadParams): LoadResult { + return try { + val currentPage = params.key ?: 1 + val users = toReqresUser( + reqresApi.getUsers( + currentPage + ).data + ) + + LoadResult.Page( + data = users, + prevKey = if (currentPage == 1) null else currentPage - 1, + nextKey = if (users.isEmpty()) null else currentPage + 1 + ) + } catch (exception: IOException) { + return LoadResult.Error(exception) + } catch (exception: HttpException) { + return LoadResult.Error(exception) + } + } +} \ No newline at end of file diff --git a/core/data/src/main/java/org/sopt/data/repository/AuthRepositoryImpl.kt b/core/data/src/main/java/org/sopt/data/repository/AuthRepositoryImpl.kt new file mode 100644 index 0000000..c183633 --- /dev/null +++ b/core/data/src/main/java/org/sopt/data/repository/AuthRepositoryImpl.kt @@ -0,0 +1,87 @@ +package org.sopt.data.repository + +import kotlinx.serialization.json.Json +import org.sopt.domain.repo.AuthRepository +import org.sopt.model.Base +import org.sopt.model.Member +import org.sopt.network.api.AuthApi +import org.sopt.network.model.request.RequestPatchPasswordDto +import org.sopt.network.model.request.RequestPostSigninDto +import org.sopt.network.model.request.RequestPostSignupDto +import org.sopt.network.model.response.base.ApiError +import org.sopt.network.model.response.base.BaseResponse +import org.sopt.network.model.response.base.toCoreModel +import org.sopt.network.model.response.base.toCoreModelNothingType +import org.sopt.network.model.response.toCoreModel +import retrofit2.HttpException +import javax.inject.Inject + +class AuthRepositoryImpl @Inject constructor( + private val authApi: AuthApi, +) : AuthRepository { + override suspend fun postSignup(member: Member, pw: String): Result> { + val result = runCatching { + authApi.postSignup( + RequestPostSignupDto( + authenticationId = member.id, + nickname = member.nickname, + password = pw, + phone = member.phone + ) + ) + } + return handleResult(result) + } + + override suspend fun getUserinfo(): Result = runCatching { + authApi.getUserinfo().data!!.toCoreModel() + } + + override suspend fun postSignin(id: String, pw: String): Result> { + val result = runCatching { + authApi.postSignin( + RequestPostSigninDto( + id, + pw + ) + ) + } + return handleResult(result) + } + + override suspend fun patchPassword( + previousPassword: String, + newPassword: String, + newPasswordVerification: String, + ): Result> { + val result = runCatching { + authApi.patchPassword( + RequestPatchPasswordDto( + previousPassword = previousPassword, + newPassword = newPassword, + newPasswordVerification = newPasswordVerification + ) + ) + } + return handleResult(result) + } + + private fun handleResult(result: Result>) = + when (val exception = result.exceptionOrNull()) { + null -> result.map { + it.toCoreModelNothingType() + } + + is HttpException -> { + val json = exception.response()?.errorBody()?.string() + val api = Json.decodeFromString(json!!) + Result.success( + api.toCoreModel() + ) + } + + else -> { + Result.failure(exception) + } + } +} \ No newline at end of file diff --git a/core/data/src/main/java/org/sopt/data/repository/UserRepositoryImpl.kt b/core/data/src/main/java/org/sopt/data/repository/UserRepositoryImpl.kt new file mode 100644 index 0000000..8e5b034 --- /dev/null +++ b/core/data/src/main/java/org/sopt/data/repository/UserRepositoryImpl.kt @@ -0,0 +1,20 @@ +package org.sopt.data.repository + +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import kotlinx.coroutines.flow.Flow +import org.sopt.data.paging.UserPagingSource +import org.sopt.domain.repo.UserRepository +import org.sopt.model.ReqresUser +import javax.inject.Inject + +class UserRepositoryImpl @Inject constructor( + private val userPagingSource: UserPagingSource, +) : UserRepository { + override fun getUser(): Flow> { + return Pager(PagingConfig(6, 2)) { + userPagingSource + }.flow + } +} \ No newline at end of file diff --git a/core/datastore/src/main/java/org/sopt/datastore/UserData.kt b/core/datastore/src/main/java/org/sopt/datastore/UserData.kt index c2e5358..e2d4b8b 100644 --- a/core/datastore/src/main/java/org/sopt/datastore/UserData.kt +++ b/core/datastore/src/main/java/org/sopt/datastore/UserData.kt @@ -4,6 +4,7 @@ import kotlinx.serialization.Serializable @Serializable data class UserData( + val memberId: Int? = null, val id: String? = null, val pw: String? = null, val name: String? = null, diff --git a/core/datastore/src/main/java/org/sopt/datastore/source/UserPreferencesDataSource.kt b/core/datastore/src/main/java/org/sopt/datastore/source/UserPreferencesDataSource.kt index f61605d..e94d0ea 100644 --- a/core/datastore/src/main/java/org/sopt/datastore/source/UserPreferencesDataSource.kt +++ b/core/datastore/src/main/java/org/sopt/datastore/source/UserPreferencesDataSource.kt @@ -14,7 +14,7 @@ class UserPreferencesDataSource @Inject constructor( suspend fun setUserData(user: UserData) { try { userPreferences.updateData { - it.copy(user.id, user.pw, user.name, user.hobby) + it.copy(user.memberId, user.id, user.pw, user.name, user.hobby) } } catch (ioException: IOException) { Log.e("exception", "setUserData ioException") @@ -31,10 +31,20 @@ class UserPreferencesDataSource @Inject constructor( } } + suspend fun setMemberId(id: Int) { + try { + userPreferences.updateData { + it.copy(memberId = id) + } + } catch (ioException: IOException) { + Log.e("exception", "setMemberId ioException") + } + } + suspend fun deleteUser() { try { userPreferences.updateData { - it.copy("","","","",false) + it.copy(null, "", "", "", "", false) } } catch (ioException: IOException) { Log.e("exception", "deleteUser ioException") diff --git a/core/domain/build.gradle.kts b/core/domain/build.gradle.kts index d56ce39..7e4d0e4 100644 --- a/core/domain/build.gradle.kts +++ b/core/domain/build.gradle.kts @@ -6,4 +6,5 @@ dependencies { implementation(projects.core.model) implementation(libs.kotlinx.coroutines.core) implementation(libs.javax.inject) + implementation(libs.androidx.paging.common) } \ No newline at end of file diff --git a/core/domain/src/main/java/org/sopt/domain/repo/AuthRepository.kt b/core/domain/src/main/java/org/sopt/domain/repo/AuthRepository.kt new file mode 100644 index 0000000..94ce8de --- /dev/null +++ b/core/domain/src/main/java/org/sopt/domain/repo/AuthRepository.kt @@ -0,0 +1,18 @@ +package org.sopt.domain.repo + +import org.sopt.model.Base +import org.sopt.model.Member + +interface AuthRepository { + suspend fun postSignup(member: Member, pw: String): Result> + + suspend fun getUserinfo(): Result + + suspend fun postSignin(id: String, pw: String): Result> + + suspend fun patchPassword( + previousPassword: String, + newPassword: String, + newPasswordVerification: String, + ): Result> +} \ No newline at end of file diff --git a/core/domain/src/main/java/org/sopt/domain/repo/UserRepository.kt b/core/domain/src/main/java/org/sopt/domain/repo/UserRepository.kt new file mode 100644 index 0000000..8c60f52 --- /dev/null +++ b/core/domain/src/main/java/org/sopt/domain/repo/UserRepository.kt @@ -0,0 +1,9 @@ +package org.sopt.domain.repo + +import androidx.paging.PagingData +import kotlinx.coroutines.flow.Flow +import org.sopt.model.ReqresUser + +interface UserRepository { + fun getUser(): Flow> +} \ No newline at end of file diff --git a/core/domain/src/main/java/org/sopt/domain/usecase/ValidatePasswordUseCase.kt b/core/domain/src/main/java/org/sopt/domain/usecase/ValidatePasswordUseCase.kt new file mode 100644 index 0000000..52ee457 --- /dev/null +++ b/core/domain/src/main/java/org/sopt/domain/usecase/ValidatePasswordUseCase.kt @@ -0,0 +1,18 @@ +package org.sopt.domain.usecase + +import org.sopt.model.ValidateResult +import org.sopt.model.getResult +import javax.inject.Inject + +class ValidatePasswordUseCase @Inject constructor() { + operator fun invoke(password: String): ValidateResult { + if (password.isBlank()) return ValidateResult.EmptyError + return getResult(passwordPattern.matches(password)) + } + + companion object { + val passwordPattern = Regex( + """^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*()-_=+{};:,<>/~`|'"\[\]\\?\.]).{8,}$""" + ) + } +} \ No newline at end of file diff --git a/core/domain/src/main/java/org/sopt/domain/usecase/ValidatePhoneNumberUseCase.kt b/core/domain/src/main/java/org/sopt/domain/usecase/ValidatePhoneNumberUseCase.kt new file mode 100644 index 0000000..4b7497c --- /dev/null +++ b/core/domain/src/main/java/org/sopt/domain/usecase/ValidatePhoneNumberUseCase.kt @@ -0,0 +1,18 @@ +package org.sopt.domain.usecase + +import org.sopt.model.ValidateResult +import org.sopt.model.getResult +import javax.inject.Inject + +class ValidatePhoneNumberUseCase @Inject constructor() { + operator fun invoke(phone: String): ValidateResult { + if (phone.isBlank()) return ValidateResult.EmptyError + return getResult(phoneNumberPattern.matches(phone)) + } + + companion object { + val phoneNumberPattern = Regex( + """^01[0-9]-[0-9]{3,4}-[0-9]{4}$""" + ) + } +} \ No newline at end of file diff --git a/core/model/src/main/java/org/sopt/model/Base.kt b/core/model/src/main/java/org/sopt/model/Base.kt new file mode 100644 index 0000000..003976a --- /dev/null +++ b/core/model/src/main/java/org/sopt/model/Base.kt @@ -0,0 +1,8 @@ +package org.sopt.model + +data class Base( + val code: Int, + val data: T? = null, + val message: String, +) + diff --git a/core/model/src/main/java/org/sopt/model/Member.kt b/core/model/src/main/java/org/sopt/model/Member.kt new file mode 100644 index 0000000..a283bdd --- /dev/null +++ b/core/model/src/main/java/org/sopt/model/Member.kt @@ -0,0 +1,7 @@ +package org.sopt.model + +data class Member( + val id: String, + val nickname: String, + val phone: String, +) diff --git a/core/model/src/main/java/org/sopt/model/ReqresUser.kt b/core/model/src/main/java/org/sopt/model/ReqresUser.kt new file mode 100644 index 0000000..3e03899 --- /dev/null +++ b/core/model/src/main/java/org/sopt/model/ReqresUser.kt @@ -0,0 +1,9 @@ +package org.sopt.model + +data class ReqresUser( + val id: Int? = null, + val email: String? = null, + val firstName: String? = null, + val lastName: String? = null, + val avatar: String? = null, +) diff --git a/core/model/src/main/java/org/sopt/model/ValidateResult.kt b/core/model/src/main/java/org/sopt/model/ValidateResult.kt new file mode 100644 index 0000000..95646e3 --- /dev/null +++ b/core/model/src/main/java/org/sopt/model/ValidateResult.kt @@ -0,0 +1,9 @@ +package org.sopt.model + +sealed class ValidateResult { + data object EmptyError : ValidateResult() + data object Error : ValidateResult() + data object Success : ValidateResult() +} + +fun getResult(isValid: Boolean) = if (isValid) ValidateResult.Success else ValidateResult.Error \ No newline at end of file diff --git a/core/network/src/main/java/org/sopt/network/api/AuthApi.kt b/core/network/src/main/java/org/sopt/network/api/AuthApi.kt new file mode 100644 index 0000000..877f6b7 --- /dev/null +++ b/core/network/src/main/java/org/sopt/network/api/AuthApi.kt @@ -0,0 +1,40 @@ +package org.sopt.network.api + +import org.sopt.network.model.request.RequestPatchPasswordDto +import org.sopt.network.model.request.RequestPostSigninDto +import org.sopt.network.model.request.RequestPostSignupDto +import org.sopt.network.model.response.ResponseGetUserInfoDto +import org.sopt.network.model.response.base.BaseResponse +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.PATCH +import retrofit2.http.POST + +interface AuthApi { + @POST("/$MEMBER/$JOIN") + suspend fun postSignup( + @Body requestPostSignupDto: RequestPostSignupDto, + ): BaseResponse + + @GET("/$MEMBER/$INFO") + suspend fun getUserinfo( + ): BaseResponse + + @POST("/$MEMBER/$LOGIN") + suspend fun postSignin( + @Body requestPostSigninDto: RequestPostSigninDto, + ): BaseResponse + + @PATCH("/$MEMBER/$PASSWORD") + suspend fun patchPassword( + @Body requestPatchPasswordDto: RequestPatchPasswordDto, + ): BaseResponse + + companion object { + private const val MEMBER = "member" + private const val JOIN = "join" + private const val INFO = "info" + private const val LOGIN = "login" + private const val PASSWORD = "password" + } +} \ No newline at end of file diff --git a/core/network/src/main/java/org/sopt/network/api/ReqresApi.kt b/core/network/src/main/java/org/sopt/network/api/ReqresApi.kt new file mode 100644 index 0000000..4876c05 --- /dev/null +++ b/core/network/src/main/java/org/sopt/network/api/ReqresApi.kt @@ -0,0 +1,17 @@ +package org.sopt.network.api + +import org.sopt.network.model.response.ResponseGetListUsersDto +import retrofit2.http.GET +import retrofit2.http.Query + +interface ReqresApi { + @GET("/$API/$USERS") + suspend fun getUsers( + @Query("page") page: Int, + ): ResponseGetListUsersDto + + companion object { + private const val API = "api" + private const val USERS = "users" + } +} \ No newline at end of file diff --git a/core/network/src/main/java/org/sopt/network/di/ApiModule.kt b/core/network/src/main/java/org/sopt/network/di/ApiModule.kt new file mode 100644 index 0000000..fbb65ec --- /dev/null +++ b/core/network/src/main/java/org/sopt/network/di/ApiModule.kt @@ -0,0 +1,24 @@ +package org.sopt.network.di + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import org.sopt.network.api.AuthApi +import org.sopt.network.api.ReqresApi +import retrofit2.Retrofit +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object ApiModule { + @Provides + @Singleton + fun provideAuthService(@Auth retrofit: Retrofit): AuthApi = + retrofit.create(AuthApi::class.java) + + @Provides + @Singleton + fun provideReqresService(@Reqres retrofit: Retrofit): ReqresApi = + retrofit.create(ReqresApi::class.java) +} \ No newline at end of file diff --git a/core/network/src/main/java/org/sopt/network/di/NetworkModule.kt b/core/network/src/main/java/org/sopt/network/di/NetworkModule.kt new file mode 100644 index 0000000..f92bd0f --- /dev/null +++ b/core/network/src/main/java/org/sopt/network/di/NetworkModule.kt @@ -0,0 +1,72 @@ +package org.sopt.network.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.Interceptor +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.sopt.network.BuildConfig.AUTH_BASE_URL +import org.sopt.network.BuildConfig.REQRES_BASE_URL +import org.sopt.network.interceptor.AuthInterceptor +import retrofit2.Retrofit +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object NetworkModule { + @Provides + @Singleton + fun provideLoggingInterceptor(): HttpLoggingInterceptor = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + + @Provides + @Singleton + fun provideAuthInterceptor(interceptor: AuthInterceptor): Interceptor = interceptor + + @Provides + @Singleton + @Auth + fun provideAuthOkHttpClient( + loggingInterceptor: HttpLoggingInterceptor, + authInterceptor: Interceptor, + ): OkHttpClient = + OkHttpClient.Builder() + .retryOnConnectionFailure(true) + .addInterceptor(authInterceptor) + .addInterceptor(loggingInterceptor) + .build() + + @Provides + @Singleton + @Reqres + fun provideReqresOkHttpClient( + loggingInterceptor: HttpLoggingInterceptor, + ): OkHttpClient = + OkHttpClient.Builder() + .addInterceptor(loggingInterceptor) + .build() + + @Provides + @Singleton + @Auth + fun provideAuthRetrofit(@Auth okHttpClient: OkHttpClient): Retrofit = Retrofit.Builder() + .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) + .baseUrl(AUTH_BASE_URL) + .client(okHttpClient) + .build() + + @Provides + @Singleton + @Reqres + fun provideReqresRetrofit(@Reqres okHttpClient: OkHttpClient): Retrofit = Retrofit.Builder() + .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) + .baseUrl(REQRES_BASE_URL) + .client(okHttpClient) + .build() +} \ No newline at end of file diff --git a/core/network/src/main/java/org/sopt/network/di/Qualifier.kt b/core/network/src/main/java/org/sopt/network/di/Qualifier.kt new file mode 100644 index 0000000..bde304d --- /dev/null +++ b/core/network/src/main/java/org/sopt/network/di/Qualifier.kt @@ -0,0 +1,11 @@ +package org.sopt.network.di + +import javax.inject.Qualifier + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class Reqres + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class Auth \ No newline at end of file diff --git a/core/network/src/main/java/org/sopt/network/interceptor/AuthInterceptor.kt b/core/network/src/main/java/org/sopt/network/interceptor/AuthInterceptor.kt new file mode 100644 index 0000000..e967d07 --- /dev/null +++ b/core/network/src/main/java/org/sopt/network/interceptor/AuthInterceptor.kt @@ -0,0 +1,26 @@ +package org.sopt.network.interceptor + +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking +import okhttp3.Interceptor +import okhttp3.Request +import okhttp3.Response +import org.sopt.datastore.source.UserPreferencesDataSource +import javax.inject.Inject + +class AuthInterceptor @Inject constructor( + private val userPreferencesDataSource: UserPreferencesDataSource, +) : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val authResponse = chain.proceed(handleRequest(chain.request())) + val memberId = authResponse.header("Location")?.toInt() + runBlocking { if (memberId != null) userPreferencesDataSource.setMemberId(memberId) } + + return authResponse + } + + private fun handleRequest(originalRequest: Request) = + originalRequest.newBuilder().addHeader("memberId", + runBlocking { userPreferencesDataSource.userData.first().memberId.toString() } + ).addHeader("Accept-Encoding", "identity").build() +} \ No newline at end of file diff --git a/core/network/src/main/java/org/sopt/network/model/request/RequestPatchPasswordDto.kt b/core/network/src/main/java/org/sopt/network/model/request/RequestPatchPasswordDto.kt new file mode 100644 index 0000000..106e680 --- /dev/null +++ b/core/network/src/main/java/org/sopt/network/model/request/RequestPatchPasswordDto.kt @@ -0,0 +1,15 @@ +package org.sopt.network.model.request + + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class RequestPatchPasswordDto( + @SerialName("newPassword") + val newPassword: String, + @SerialName("newPasswordVerification") + val newPasswordVerification: String, + @SerialName("previousPassword") + val previousPassword: String, +) \ No newline at end of file diff --git a/core/network/src/main/java/org/sopt/network/model/request/RequestPostSigninDto.kt b/core/network/src/main/java/org/sopt/network/model/request/RequestPostSigninDto.kt new file mode 100644 index 0000000..34cc5da --- /dev/null +++ b/core/network/src/main/java/org/sopt/network/model/request/RequestPostSigninDto.kt @@ -0,0 +1,13 @@ +package org.sopt.network.model.request + + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class RequestPostSigninDto( + @SerialName("authenticationId") + val authenticationId: String, + @SerialName("password") + val password: String, +) \ No newline at end of file diff --git a/core/network/src/main/java/org/sopt/network/model/request/RequestPostSignupDto.kt b/core/network/src/main/java/org/sopt/network/model/request/RequestPostSignupDto.kt new file mode 100644 index 0000000..b1a32d1 --- /dev/null +++ b/core/network/src/main/java/org/sopt/network/model/request/RequestPostSignupDto.kt @@ -0,0 +1,17 @@ +package org.sopt.network.model.request + + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class RequestPostSignupDto( + @SerialName("authenticationId") + val authenticationId: String, + @SerialName("nickname") + val nickname: String, + @SerialName("password") + val password: String, + @SerialName("phone") + val phone: String, +) \ No newline at end of file diff --git a/core/network/src/main/java/org/sopt/network/model/response/ResponseGetListUsersDto.kt b/core/network/src/main/java/org/sopt/network/model/response/ResponseGetListUsersDto.kt new file mode 100644 index 0000000..0c921f3 --- /dev/null +++ b/core/network/src/main/java/org/sopt/network/model/response/ResponseGetListUsersDto.kt @@ -0,0 +1,53 @@ +package org.sopt.network.model.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.sopt.model.ReqresUser + +@Serializable +data class ResponseGetListUsersDto( + @SerialName("page") + val page: Int, + @SerialName("per_page") + val perPage: Int, + @SerialName("total") + val total: Int, + @SerialName("total_pages") + val totalPages: Int, + @SerialName("data") + val data: List, + @SerialName("support") + val support: Support, +) { + @Serializable + data class UserData( + @SerialName("id") + val id: Int? = null, + @SerialName("email") + val email: String? = null, + @SerialName("first_name") + val firstName: String? = null, + @SerialName("last_name") + val lastName: String? = null, + @SerialName("avatar") + val avatar: String? = null, + ) + + @Serializable + data class Support( + @SerialName("url") + val url: String, + @SerialName("text") + val text: String, + ) +} + +fun toReqresUser(data: List): List = data.map { it -> + ReqresUser( + id = it.id, + email = it.email, + firstName = it.firstName, + lastName = it.lastName, + avatar = it.avatar + ) +} diff --git a/core/network/src/main/java/org/sopt/network/model/response/ResponseGetUserInfoDto.kt b/core/network/src/main/java/org/sopt/network/model/response/ResponseGetUserInfoDto.kt new file mode 100644 index 0000000..52e1fde --- /dev/null +++ b/core/network/src/main/java/org/sopt/network/model/response/ResponseGetUserInfoDto.kt @@ -0,0 +1,22 @@ +package org.sopt.network.model.response + + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.sopt.model.Member + +@Serializable +data class ResponseGetUserInfoDto( + @SerialName("authenticationId") + val authenticationId: String, + @SerialName("nickname") + val nickname: String, + @SerialName("phone") + val phone: String, +) + +fun ResponseGetUserInfoDto.toCoreModel() = Member( + id = this.authenticationId, + nickname = this.nickname, + phone = this.phone, +) \ No newline at end of file diff --git a/core/network/src/main/java/org/sopt/network/model/response/base/ApiError.kt b/core/network/src/main/java/org/sopt/network/model/response/base/ApiError.kt new file mode 100644 index 0000000..19b39de --- /dev/null +++ b/core/network/src/main/java/org/sopt/network/model/response/base/ApiError.kt @@ -0,0 +1,15 @@ +package org.sopt.network.model.response.base + +import kotlinx.serialization.Serializable +import org.sopt.model.Base + +@Serializable +data class ApiError( + val code: Int, + val message: String, +) + +fun ApiError.toCoreModel(): Base = Base( + code = this.code, + message = this.message +) diff --git a/core/network/src/main/java/org/sopt/network/model/response/base/BaseResponse.kt b/core/network/src/main/java/org/sopt/network/model/response/base/BaseResponse.kt new file mode 100644 index 0000000..e71ca43 --- /dev/null +++ b/core/network/src/main/java/org/sopt/network/model/response/base/BaseResponse.kt @@ -0,0 +1,22 @@ +package org.sopt.network.model.response.base + +import kotlinx.serialization.Serializable +import org.sopt.model.Base + +@Serializable +data class BaseResponse( + val code: Int, + val data: T? = null, + val message: String, +) + +fun BaseResponse.toCoreModel(): Base = Base( + code = this.code, + data = this.data, + message = this.message +) + +fun BaseResponse.toCoreModelNothingType(): Base = Base( + code = this.code, + message = this.message +) \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fba8a9a..f4b3dd8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -25,6 +25,8 @@ androidx-fragment-ktx = "1.6.2" androidx-recycler-view = "1.3.2" androidx-splash-screen = "1.0.1" androidx-datastore = "1.0.0" +androidx-paging = "3.2.1" +androidx-paging-compose = "3.3.0-rc01" kotlin = "1.9.10" kotlinx-coroutines = "1.7.3" @@ -136,6 +138,9 @@ androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", ve androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "androidx-fragment-ktx" } androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "androidx-recycler-view" } androidx-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidx-splash-screen" } +androidx-paging = { group = "androidx.paging", name = "paging-runtime", version.ref = "androidx-paging" } +androidx-paging-common = { group = "androidx.paging", name = "paging-common", version.ref = "androidx-paging" } +androidx-paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "androidx-paging-compose" } google-material = { group = "com.google.android.material", name = "material", version.ref = "material" }