diff --git a/.gradle/8.4/executionHistory/executionHistory.bin b/.gradle/8.4/executionHistory/executionHistory.bin index 4aec9105..34468448 100644 Binary files a/.gradle/8.4/executionHistory/executionHistory.bin and b/.gradle/8.4/executionHistory/executionHistory.bin differ diff --git a/.gradle/8.4/executionHistory/executionHistory.lock b/.gradle/8.4/executionHistory/executionHistory.lock index 951ed17a..633e6d1f 100644 Binary files a/.gradle/8.4/executionHistory/executionHistory.lock and b/.gradle/8.4/executionHistory/executionHistory.lock differ diff --git a/.gradle/8.4/fileHashes/fileHashes.bin b/.gradle/8.4/fileHashes/fileHashes.bin index 5ffc93c7..af07c8ed 100644 Binary files a/.gradle/8.4/fileHashes/fileHashes.bin and b/.gradle/8.4/fileHashes/fileHashes.bin differ diff --git a/.gradle/8.4/fileHashes/fileHashes.lock b/.gradle/8.4/fileHashes/fileHashes.lock index b12e51b1..ac40f60a 100644 Binary files a/.gradle/8.4/fileHashes/fileHashes.lock and b/.gradle/8.4/fileHashes/fileHashes.lock differ diff --git a/.gradle/8.4/fileHashes/resourceHashesCache.bin b/.gradle/8.4/fileHashes/resourceHashesCache.bin index ba4a5f31..c63d8073 100644 Binary files a/.gradle/8.4/fileHashes/resourceHashesCache.bin and b/.gradle/8.4/fileHashes/resourceHashesCache.bin differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index dfa7a2ef..be549be6 100644 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin index 23a4bd3d..154d1646 100644 Binary files a/.gradle/buildOutputCleanup/outputFiles.bin and b/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/buildSrc/.gradle/8.4/executionHistory/executionHistory.lock b/buildSrc/.gradle/8.4/executionHistory/executionHistory.lock index c009801e..a9471080 100644 Binary files a/buildSrc/.gradle/8.4/executionHistory/executionHistory.lock and b/buildSrc/.gradle/8.4/executionHistory/executionHistory.lock differ diff --git a/data/src/main/java/com/example/data/di/MypageUserInfoModule.kt b/data/src/main/java/com/example/data/di/MypageUserInfoModule.kt new file mode 100644 index 00000000..43c1b43f --- /dev/null +++ b/data/src/main/java/com/example/data/di/MypageUserInfoModule.kt @@ -0,0 +1,52 @@ +package com.example.data.di + +import com.example.data.mapper.mypage.MypageUserInfoMapper +import com.example.data.repository.local.auth.AuthTokenDataSource +import com.example.data.repository.remote.mypage.MypageUserInfoRemoteDataSource +import com.example.data.repository.remote.mypage.MypageUserInfoRemoteDataSourceImpl +import com.example.data.repository.remote.mypage.MypageUserInfoRepositoryImpl +import com.example.data.retrofit.MypageUserInfoService +import com.example.domain.repository.mypage.MypageUserInfoRepository +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import retrofit2.Retrofit +import javax.inject.Singleton + +@Module(includes = [NetworkModule::class, DataStoreModule::class]) +@InstallIn(SingletonComponent::class) +class MypageUserInfoModule { + @Singleton + @Provides + fun provideMypageUserInfoService(retrofit: Retrofit): MypageUserInfoService { + return retrofit.create(MypageUserInfoService::class.java) + } + + @Provides + @Singleton + fun provideMypageUserInfoDataSource( + mypageUserInfoService: MypageUserInfoService, + authTokenDataSource: AuthTokenDataSource + ): MypageUserInfoRemoteDataSource { + return MypageUserInfoRemoteDataSourceImpl(mypageUserInfoService, authTokenDataSource) + } + + @Provides + @Singleton + fun provideMypageUserInfoRepository( + mypageUserInfoRemoteDataSource: MypageUserInfoRemoteDataSource, + mypageUserInfoMapper: MypageUserInfoMapper + ): MypageUserInfoRepository { + return MypageUserInfoRepositoryImpl( + mypageUserInfoRemoteDataSource, + mypageUserInfoMapper + ) + } + + @Provides + @Singleton + fun provideMypageUserInfoMapper(): MypageUserInfoMapper { + return MypageUserInfoMapper() + } +} \ No newline at end of file diff --git a/data/src/main/java/com/example/data/mapper/mypage/MypageUserInfoMapper.kt b/data/src/main/java/com/example/data/mapper/mypage/MypageUserInfoMapper.kt new file mode 100644 index 00000000..ca7ce788 --- /dev/null +++ b/data/src/main/java/com/example/data/mapper/mypage/MypageUserInfoMapper.kt @@ -0,0 +1,18 @@ +package com.example.data.mapper.mypage + +import com.example.data.model.mypage.MypageUserInfoDto +import com.example.domain.mapper.Mapper +import com.example.domain.model.mypage.MypageUserInfo + +class MypageUserInfoMapper:Mapper { + override fun mapFromEntity(type: MypageUserInfoDto): MypageUserInfo { + return MypageUserInfo( + id = type.id, + imageUrl = type.imageUrl, + nickname = type.nickname, + userLevel = type.userLevel, + veganType = type.veganType, + point = type.point + ) + } +} \ No newline at end of file diff --git a/data/src/main/java/com/example/data/model/mypage/MypageUserInfoResponse.kt b/data/src/main/java/com/example/data/model/mypage/MypageUserInfoResponse.kt new file mode 100644 index 00000000..ff77a69c --- /dev/null +++ b/data/src/main/java/com/example/data/model/mypage/MypageUserInfoResponse.kt @@ -0,0 +1,19 @@ +package com.example.data.model.mypage + +import com.squareup.moshi.Json + +data class MypageUserInfoResponse( + @Json(name = "check") + val check: Boolean, + @Json(name = "information") + val information: MypageUserInfoDto +) + +data class MypageUserInfoDto( + @Json(name = "id") val id: Int, + @Json(name = "imageUrl") val imageUrl:String, + @Json(name = "nickname") val nickname:String, + @Json(name = "userLevel") val userLevel:String, + @Json(name = "veganType") val veganType:String, + @Json(name = "point") val point:Int +) diff --git a/data/src/main/java/com/example/data/repository/remote/mypage/MypageUserInfoRemoteDataSource.kt b/data/src/main/java/com/example/data/repository/remote/mypage/MypageUserInfoRemoteDataSource.kt new file mode 100644 index 00000000..7afd5c08 --- /dev/null +++ b/data/src/main/java/com/example/data/repository/remote/mypage/MypageUserInfoRemoteDataSource.kt @@ -0,0 +1,8 @@ +package com.example.data.repository.remote.mypage + +import com.example.data.model.mypage.MypageUserInfoResponse +import com.skydoves.sandwich.ApiResponse + +interface MypageUserInfoRemoteDataSource { + suspend fun getMypageUserInfo(): ApiResponse +} \ No newline at end of file diff --git a/data/src/main/java/com/example/data/repository/remote/mypage/MypageUserInfoRemoteDataSourceImpl.kt b/data/src/main/java/com/example/data/repository/remote/mypage/MypageUserInfoRemoteDataSourceImpl.kt new file mode 100644 index 00000000..ea27921a --- /dev/null +++ b/data/src/main/java/com/example/data/repository/remote/mypage/MypageUserInfoRemoteDataSourceImpl.kt @@ -0,0 +1,29 @@ +package com.example.data.repository.remote.mypage + +import com.example.data.model.mypage.MypageUserInfoResponse +import com.example.data.repository.local.auth.AuthTokenDataSource +import com.example.data.retrofit.MypageUserInfoService +import com.skydoves.sandwich.ApiResponse +import com.skydoves.sandwich.retrofit.errorBody +import com.skydoves.sandwich.suspendOnError +import com.skydoves.sandwich.suspendOnSuccess +import kotlinx.coroutines.flow.first +import timber.log.Timber +import javax.inject.Inject + +class MypageUserInfoRemoteDataSourceImpl @Inject constructor( + private val mypageUserInfoService: MypageUserInfoService, + private val authTokenDataSource: AuthTokenDataSource +):MypageUserInfoRemoteDataSource { + override suspend fun getMypageUserInfo(): ApiResponse { + val accessToken = authTokenDataSource.accessToken.first() + val authHeader = "Bearer $accessToken" + return mypageUserInfoService.getMypageUserInfo(authHeader).suspendOnSuccess { + Timber.d("getMypageUserInfo successful") + ApiResponse.Success(this.data) + }.suspendOnError { + Timber.e("getMypageUserInfo error: ${this.errorBody}") + ApiResponse.Failure.Error(this.errorBody) + } + } +} \ No newline at end of file diff --git a/data/src/main/java/com/example/data/repository/remote/mypage/MypageUserInfoRepositoryImpl.kt b/data/src/main/java/com/example/data/repository/remote/mypage/MypageUserInfoRepositoryImpl.kt new file mode 100644 index 00000000..a85b1d7a --- /dev/null +++ b/data/src/main/java/com/example/data/repository/remote/mypage/MypageUserInfoRepositoryImpl.kt @@ -0,0 +1,39 @@ +package com.example.data.repository.remote.mypage + +import com.example.data.mapper.mypage.MypageUserInfoMapper +import com.example.domain.model.mypage.MypageUserInfo +import com.example.domain.repository.mypage.MypageUserInfoRepository +import com.skydoves.sandwich.ApiResponse +import com.skydoves.sandwich.retrofit.errorBody +import timber.log.Timber +import javax.inject.Inject + +class MypageUserInfoRepositoryImpl @Inject constructor( + private val mypageUserInfoRemoteDataSource: MypageUserInfoRemoteDataSource, + private val mypageUserInfoMapper: MypageUserInfoMapper +):MypageUserInfoRepository{ + override suspend fun getMypageUserInfo(): Result { + return try { + val response = mypageUserInfoRemoteDataSource.getMypageUserInfo() + when (response) { + is ApiResponse.Success -> { + val magazineList = mypageUserInfoMapper.mapFromEntity(response.data.information) + Result.success(magazineList) + } + + is ApiResponse.Failure.Error -> { + Timber.e("getMypageUserInfo error: ${response.errorBody}") + Result.failure(Exception("getMypageUserInfo failed")) + } + + is ApiResponse.Failure.Exception -> { + Timber.e("getMypageUserInfo exception: ${response.message}") + Result.failure(response.throwable) + } + } + } catch (e: Exception) { + Timber.e(e, "getMypageUserInfo exception") + Result.failure(e) + } + } +} \ No newline at end of file diff --git a/data/src/main/java/com/example/data/retrofit/MypageUserInfoService.kt b/data/src/main/java/com/example/data/retrofit/MypageUserInfoService.kt new file mode 100644 index 00000000..6a8e93bf --- /dev/null +++ b/data/src/main/java/com/example/data/retrofit/MypageUserInfoService.kt @@ -0,0 +1,13 @@ +package com.example.data.retrofit + +import com.example.data.model.mypage.MypageUserInfoResponse +import com.skydoves.sandwich.ApiResponse +import retrofit2.http.GET +import retrofit2.http.Header + +interface MypageUserInfoService { + @GET("/api/v1/users/my-page") + suspend fun getMypageUserInfo( + @Header("Authorization") token: String + ): ApiResponse +} \ No newline at end of file diff --git a/domain/src/main/java/com/example/domain/model/mypage/MypageUserInfo.kt b/domain/src/main/java/com/example/domain/model/mypage/MypageUserInfo.kt new file mode 100644 index 00000000..c4e81832 --- /dev/null +++ b/domain/src/main/java/com/example/domain/model/mypage/MypageUserInfo.kt @@ -0,0 +1,10 @@ +package com.example.domain.model.mypage + +data class MypageUserInfo( + val id: Int, + val imageUrl:String, + val nickname:String, + val userLevel:String, + val veganType:String, + val point:Int +) diff --git a/domain/src/main/java/com/example/domain/repository/mypage/MypageUserInfoRepository.kt b/domain/src/main/java/com/example/domain/repository/mypage/MypageUserInfoRepository.kt new file mode 100644 index 00000000..6ddb2490 --- /dev/null +++ b/domain/src/main/java/com/example/domain/repository/mypage/MypageUserInfoRepository.kt @@ -0,0 +1,7 @@ +package com.example.domain.repository.mypage + +import com.example.domain.model.mypage.MypageUserInfo + +interface MypageUserInfoRepository { + suspend fun getMypageUserInfo(): Result +} \ No newline at end of file diff --git a/domain/src/main/java/com/example/domain/useCase/mypage/MypageUserInfoUseCase.kt b/domain/src/main/java/com/example/domain/useCase/mypage/MypageUserInfoUseCase.kt new file mode 100644 index 00000000..9844d74f --- /dev/null +++ b/domain/src/main/java/com/example/domain/useCase/mypage/MypageUserInfoUseCase.kt @@ -0,0 +1,13 @@ +package com.example.domain.useCase.mypage + +import com.example.domain.model.mypage.MypageUserInfo +import com.example.domain.repository.mypage.MypageUserInfoRepository +import javax.inject.Inject + +class MypageUserInfoUseCase @Inject constructor( + private val mypageUserInfoRepository: MypageUserInfoRepository +){ + suspend operator fun invoke():Result { + return mypageUserInfoRepository.getMypageUserInfo() + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/example/presentation/util/UserLevelDrawables.kt b/presentation/src/main/java/com/example/presentation/util/UserLevelDrawables.kt new file mode 100644 index 00000000..02fe1756 --- /dev/null +++ b/presentation/src/main/java/com/example/presentation/util/UserLevelDrawables.kt @@ -0,0 +1,30 @@ +package com.example.presentation.util + +import android.content.Context +import android.graphics.drawable.Drawable +import androidx.core.content.ContextCompat +import com.example.presentation.R + +data class UserLevelIllusts( + val context:Context, + val userLevelIllus:List = listOf( + ContextCompat.getDrawable(context, R.drawable.illus_user_level_1_seed), + ContextCompat.getDrawable(context, R.drawable.illus_user_level_2_cotyledon), + ContextCompat.getDrawable(context, R.drawable.illus_user_level_3_sprout), + ContextCompat.getDrawable(context, R.drawable.illus_user_level_4_stem), + ContextCompat.getDrawable(context, R.drawable.illus_user_level_5_leaf), + ContextCompat.getDrawable(context, R.drawable.illus_user_level_6_tree), + ContextCompat.getDrawable(context, R.drawable.illus_user_level_7_flower), + ContextCompat.getDrawable(context, R.drawable.illus_user_level_8_fruit) + ), + val userLevelIcons:List = listOf( + ContextCompat.getDrawable(context, R.drawable.ic_user_level_1_seed), + ContextCompat.getDrawable(context, R.drawable.ic_user_level_2_cotyledon), + ContextCompat.getDrawable(context, R.drawable.ic_user_level_3_sprout), + ContextCompat.getDrawable(context, R.drawable.ic_user_level_4_stem), + ContextCompat.getDrawable(context, R.drawable.ic_user_level_5_leaf), + ContextCompat.getDrawable(context, R.drawable.ic_user_level_6_tree), + ContextCompat.getDrawable(context, R.drawable.ic_user_level_7_flower), + ContextCompat.getDrawable(context, R.drawable.ic_user_level_8_fruit), + ) +) diff --git a/presentation/src/main/java/com/example/presentation/view/mypage/view/MainMypageFragment.kt b/presentation/src/main/java/com/example/presentation/view/mypage/view/MainMypageFragment.kt index 17591e1f..3f96ab4a 100644 --- a/presentation/src/main/java/com/example/presentation/view/mypage/view/MainMypageFragment.kt +++ b/presentation/src/main/java/com/example/presentation/view/mypage/view/MainMypageFragment.kt @@ -1,13 +1,24 @@ package com.example.presentation.view.mypage.view import android.widget.ArrayAdapter +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.CircleCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.example.domain.model.mypage.MypageUserInfo import com.example.presentation.R import com.example.presentation.base.BaseFragment import com.example.presentation.config.navigation.main.MainNavigationHandler import com.example.presentation.databinding.FragmentMainMypageBinding import com.example.presentation.util.DrawerController import com.example.presentation.util.MypageUserLevelExplainDialog +import com.example.presentation.util.UserLevelIllusts +import com.example.presentation.view.mypage.viewModel.MypageUserInfoViewModel import dagger.hilt.android.AndroidEntryPoint import timber.log.Timber import javax.inject.Inject @@ -116,41 +127,51 @@ class MainMypageFragment : BaseFragment(R.layout.frag @Inject lateinit var drawerController: DrawerController - @Inject lateinit var mainNavigationHandler: MainNavigationHandler + private val mypageUserInfoViewModel:MypageUserInfoViewModel by viewModels() override fun init() { - Timber.d("mypage init") - Timber.d("findNavController().backQueue.size:${findNavController().backQueue.size}") - - binding.llUserLevelExplain.setOnClickListener { - openDialogUserLevelExplain() - } + binding.lifecycleOwner = this setOpenDrawer() + openDialogUserLevelExplain() setProgressBar(5, 1) setVeganTypeDropdown(getString(R.string.vegan_type_unknown)) + moveFuns() - binding.llEditProfile.setOnClickListener { - mainNavigationHandler.navigateToEditProfile() - } - binding.llMyReview.setOnClickListener { - mainNavigationHandler.navigateToReview() - } - binding.llMyRestaurant.setOnClickListener { - mainNavigationHandler.navigateToMyRestaurant() - } - binding.llMyMagazine.setOnClickListener { - mainNavigationHandler.navigateToMyMagazine() - } - binding.llMyRecipe.setOnClickListener { - mainNavigationHandler.navigateToMyRecipe() - } - binding.llSetting.setOnClickListener { - mainNavigationHandler.navigateToMySetting() + getUserInfo() + } + + private fun getUserInfo(){ + mypageUserInfoViewModel.userInfo.observe(this){ + setUserInfo(it) } } + private fun setUserInfo(userInfo:MypageUserInfo){ + val userLevelKr = requireContext().resources.getStringArray(R.array.user_levels_kr) + val userLevelEng = requireContext().resources.getStringArray(R.array.user_levels_eng) + val levelIllusts = UserLevelIllusts(requireContext()).userLevelIllus + val levelIcons = UserLevelIllusts(requireContext()).userLevelIcons + + val index = userLevelEng.indexOf(userInfo.userLevel) + Glide.with(this) + .load(levelIllusts[index]) + .into(binding.ivIllusUserLevel) + binding.tvUserLevel.text = "${userLevelKr[index]} 레벨" + + Glide.with(this) + .load(userInfo.imageUrl) + .transform(CircleCrop()) + .into(binding.ivUserProfileImg) + + Glide.with(this) + .load(levelIcons[index]) + .into(binding.ivUserProfileUserLevel) + + binding.tvUserName.text = userInfo.nickname + + } private fun setOpenDrawer() { binding.includedToolbar.ibNotification.setOnClickListener { @@ -159,8 +180,10 @@ class MainMypageFragment : BaseFragment(R.layout.frag } private fun openDialogUserLevelExplain() { - val dialog = MypageUserLevelExplainDialog(requireContext()) - dialog.show() + binding.llUserLevelExplain.setOnClickListener { + val dialog = MypageUserLevelExplainDialog(requireContext()) + dialog.show() + } } private fun setProgressBar(maxInt: Int, nowGauge: Int) { @@ -192,4 +215,26 @@ class MainMypageFragment : BaseFragment(R.layout.frag // binding.sSetVeganType.adapter = dropdownAdapter } + private fun moveFuns(){ + + binding.llEditProfile.setOnClickListener { + mainNavigationHandler.navigateToEditProfile() + } + binding.llMyReview.setOnClickListener { + mainNavigationHandler.navigateToReview() + } + binding.llMyRestaurant.setOnClickListener { + mainNavigationHandler.navigateToMyRestaurant() + } + binding.llMyMagazine.setOnClickListener { + mainNavigationHandler.navigateToMyMagazine() + } + binding.llMyRecipe.setOnClickListener { + mainNavigationHandler.navigateToMyRecipe() + } + binding.llSetting.setOnClickListener { + mainNavigationHandler.navigateToMySetting() + } + } + } \ No newline at end of file diff --git a/presentation/src/main/java/com/example/presentation/view/mypage/viewModel/MypageUserInfoViewModel.kt b/presentation/src/main/java/com/example/presentation/view/mypage/viewModel/MypageUserInfoViewModel.kt new file mode 100644 index 00000000..bdee6440 --- /dev/null +++ b/presentation/src/main/java/com/example/presentation/view/mypage/viewModel/MypageUserInfoViewModel.kt @@ -0,0 +1,34 @@ +package com.example.presentation.view.mypage.viewModel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.domain.model.mypage.MypageUserInfo +import com.example.domain.useCase.mypage.MypageUserInfoUseCase +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject + +@HiltViewModel +class MypageUserInfoViewModel @Inject constructor( + private val mypageUserInfoUseCase: MypageUserInfoUseCase +):ViewModel(){ + + private val _userInfo = MutableLiveData() + val userInfo:LiveData = _userInfo + + init { + getUserInfo() + } + private fun getUserInfo(){ + viewModelScope.launch { + mypageUserInfoUseCase.invoke().onSuccess { + _userInfo.value = it + }.onFailure { + Timber.e(it.message) + } + } + } +} \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_main_mypage.xml b/presentation/src/main/res/layout/fragment_main_mypage.xml index 1b9db323..b5e9f385 100644 --- a/presentation/src/main/res/layout/fragment_main_mypage.xml +++ b/presentation/src/main/res/layout/fragment_main_mypage.xml @@ -36,10 +36,10 @@ android:layout_width="@dimen/item_160" android:layout_height="@dimen/item_160" android:layout_marginTop="@dimen/margin_20" - android:background="@drawable/illus_user_level_1_seed" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/v_divider_under_tool_bar" /> + app:layout_constraintTop_toBottomOf="@id/v_divider_under_tool_bar" + app:layout_constraintBottom_toTopOf="@id/tv_user_level"/> diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 80746a90..0a5e3d9c 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -151,6 +151,26 @@ 나무 열매 + + 씨앗 + 떡잎 + 새싹 + 줄기 + 잎사귀 + 나무 + + 열매 + + + SEED + ROOT + SPROUT + STEM + LEAF + TREE + FLOWER + FRUIT + 닫기