Skip to content

Commit

Permalink
[Feature] 수집 도감 UI 및 기능 구현 (#115)
Browse files Browse the repository at this point in the history
* [feat] : 매칭시 dataStore의 mbtiCollection 추가

* [feat] : 아이콘 파일 추가

* [build] : collection 모듈 생성

* [feat] : Mbti Collection 도메인 레이어 제작

* [refactor] : mbti 컬렉션 추가 로직 분리

* [feat] : Mbti 컬렉션 데이터 레이어 제작 및 UseCase, Repository DI 생성

* [test] : Mbti 컬렉션 데이터 레이어 테스트 코드 제작

* [refactor] : Mbti 도메인 Entity 순서 정리

* [feat] : 컬렉션 UI 및 로직 구현

* [feat] : 컬렉션 페이지 네비게이션 정의

* [feat] : activeMbtiBadge, inactiveMbtiBadge 아이콘 찾는 함수 제작

* [feat] : 홈 화면 컬렉션 카드 추가 및 네비게이션 구현

* [chore] : reformat

* [refactor] : 리뷰 반영 - flow에서 result로 변경

* [test] : 테스트 코드 수정
  • Loading branch information
ham2174 authored Feb 28, 2024
1 parent 5dc0aa5 commit e659c76
Show file tree
Hide file tree
Showing 61 changed files with 1,385 additions and 21 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ dependencies {
implementation(projects.feature.home)
implementation(projects.feature.match)
implementation(projects.feature.onboarding)
implementation(projects.feature.collection)
// implementation(libs.coil.core)
implementation(libs.startup)
// implementation(libs.security)
Expand Down
32 changes: 32 additions & 0 deletions app/src/main/java/com/moya/funch/di/MbtiCollectionModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.moya.funch.di

import com.moya.funch.repository.MbtiCollectionRepository
import com.moya.funch.repository.MbtiCollectionRepositoryImpl
import com.moya.funch.usecase.LoadMbtiCollectionUseCase
import com.moya.funch.usecase.LoadMbtiCollectionUseCaseImpl
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

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

@Module
@InstallIn(SingletonComponent::class)
interface UseCaseBinder {
@Binds
@Singleton
fun bindLoadMbtiCollectionUseCase(useCase: LoadMbtiCollectionUseCaseImpl): LoadMbtiCollectionUseCase
}

@Module
@InstallIn(SingletonComponent::class)
interface RepositoryBinder {
@Binds
@Singleton
fun bindMbtiCollectionRepository(repository: MbtiCollectionRepositoryImpl): MbtiCollectionRepository
}
}
6 changes: 5 additions & 1 deletion app/src/main/java/com/moya/funch/navigation/FunchNavHost.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navOptions
import com.moya.funch.collection.navigation.collectionScreen
import com.moya.funch.collection.navigation.navigateToCollection
import com.moya.funch.match.navigation.matchingScreen
import com.moya.funch.match.navigation.navigateToMatching
import com.moya.funch.onboarding.navigation.ON_BOARDING_ROUTE
Expand All @@ -26,10 +28,12 @@ fun FunchNavHost(hasProfile: Boolean, navController: NavHostController = remembe
)
homeScreen(
onNavigateToMatching = ::onNavigateToMatching,
onNavigateToMyProfile = ::onNavigateToMyProfile
onNavigateToMyProfile = ::onNavigateToMyProfile,
onNavigateToCollection = ::navigateToCollection
)
matchingScreen(onClose = { popBackStack(HOME_ROUTE, false) })
onBoardingScreen(onNavigateToCreateProfile = ::navigateToCreateProfile)
collectionScreen(onNavigateToHome = ::onNavigateToHome)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ import com.moya.funch.model.ProfileModel

interface LocalUserDataSource : UserDataSource {
suspend fun saveUserProfile(userModel: ProfileModel): Result<Unit>

suspend fun fetchUserMbtiCollection(): Result<Set<String>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,6 @@ class LocalUserDataSourceImpl @Inject constructor(
}
}
}

override suspend fun fetchUserMbtiCollection(): Result<Set<String>> = runCatching { userDataStore.mbtiCollection }
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,16 @@ class RemoteMatchDataSourceImpl @Inject constructor(
targetCode = targetCode
)
)
}.mapCatching { it.data }
}.mapCatching {
saveMbtiCollection(it.data.profile.mbti)
it.data
}
}

private fun saveMbtiCollection(mbti: String) {
dataStore.mbtiCollection = dataStore.mbtiCollection.toMutableSet().apply {
add(mbti)
}.toSet()
}

override suspend fun canMatchProfile(targetCode: String): Result<Boolean> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.moya.funch.repository

import com.moya.funch.datasource.local.LocalUserDataSource
import com.moya.funch.entity.Mbti
import javax.inject.Inject

class MbtiCollectionRepositoryImpl @Inject constructor(
private val localUserDataSource: LocalUserDataSource
) : MbtiCollectionRepository {
override suspend fun addMbtiCollection(): Result<Unit> {
// @Gun Hyung Todo : 추후 데이터 레이어 리팩토링 이후 작업 진행
return Result.success(Unit)
}

override suspend fun loadMbtiCollection(): Result<List<Mbti>> {
return localUserDataSource.fetchUserMbtiCollection().mapCatching { mbtiList ->
mbtiList.map { mbti -> Mbti.valueOf(mbti) }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,20 @@ internal class LocalUserDataSourceImplTest {
)
}

@Test
fun `MbtiCollection이 비어있으면 emptySet를 가져온다`() = runTest {
// given
coEvery { userDataStore.mbtiCollection } returns emptySet()
// when
val actualResult = localUserDataSource.fetchUserMbtiCollection()
// then
assertAll(
{ coVerify(exactly = 1) { userDataStore.mbtiCollection } },
{ assertThat(actualResult.isSuccess).isTrue() },
{ assertThat(actualResult.getOrNull()).isEmpty() }
)
}

companion object {
@JvmField
@RegisterExtension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface UserDataStore {
var subwayName: String
var subwayLines: Set<String>
var mbti: String
var mbtiCollection: Set<String>

fun hasUserCode(): Boolean

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ class UserDataStoreImpl @Inject constructor(
}
}

override var mbtiCollection: Set<String>
get() = preferences.getStringSet(MBTI_COLLECTION, setOf()).orEmpty()
set(value) {
preferences.edit(commit = true) {
putStringSet(MBTI_COLLECTION, value)
}
}

override fun hasUserCode(): Boolean {
return preferences.contains(USER_CODE)
}
Expand Down Expand Up @@ -123,5 +131,6 @@ class UserDataStoreImpl @Inject constructor(
const val SUBWAY_NAME = "SUBWAY_NAME"
const val SUBWAY_LINE = "SUBWAY_LINE"
const val MBTI = "MBTI"
const val MBTI_COLLECTION = "MBTI_COLLECTION"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,45 @@ fun subwayLinePainter(value: String): Painter = when (value) {
"INCHEON_TWO" -> painterResource(id = FunchIconAsset.SubwayLine.subway_line_incheon_two)
else -> throw IllegalArgumentException("Unknown Icon: $value")
}

@Composable
fun activeMbtiBadgePainter(value: String): Painter = when (value) {
"ISTJ" -> painterResource(id = FunchIconAsset.MbtiBadge.istj_active)
"ISFJ" -> painterResource(id = FunchIconAsset.MbtiBadge.isfj_active)
"INFJ" -> painterResource(id = FunchIconAsset.MbtiBadge.infj_active)
"INTJ" -> painterResource(id = FunchIconAsset.MbtiBadge.intj_active)
"ISTP" -> painterResource(id = FunchIconAsset.MbtiBadge.istp_active)
"ISFP" -> painterResource(id = FunchIconAsset.MbtiBadge.isfp_active)
"INFP" -> painterResource(id = FunchIconAsset.MbtiBadge.infp_active)
"INTP" -> painterResource(id = FunchIconAsset.MbtiBadge.intp_active)
"ESTP" -> painterResource(id = FunchIconAsset.MbtiBadge.estp_active)
"ESFP" -> painterResource(id = FunchIconAsset.MbtiBadge.esfp_active)
"ENFP" -> painterResource(id = FunchIconAsset.MbtiBadge.enfp_active)
"ENTP" -> painterResource(id = FunchIconAsset.MbtiBadge.entp_active)
"ESTJ" -> painterResource(id = FunchIconAsset.MbtiBadge.estj_active)
"ESFJ" -> painterResource(id = FunchIconAsset.MbtiBadge.esfj_active)
"ENFJ" -> painterResource(id = FunchIconAsset.MbtiBadge.enfj_active)
"ENTJ" -> painterResource(id = FunchIconAsset.MbtiBadge.entj_active)
else -> throw IllegalArgumentException("Unknown Icon: $value")
}

@Composable
fun inactiveMbtiBadgePainter(value: String): Painter = when (value) {
"ISTJ" -> painterResource(id = FunchIconAsset.MbtiBadge.istj_inactive)
"ISFJ" -> painterResource(id = FunchIconAsset.MbtiBadge.isfj_inactive)
"INFJ" -> painterResource(id = FunchIconAsset.MbtiBadge.infj_inactive)
"INTJ" -> painterResource(id = FunchIconAsset.MbtiBadge.intj_inactive)
"ISTP" -> painterResource(id = FunchIconAsset.MbtiBadge.istp_inactive)
"ISFP" -> painterResource(id = FunchIconAsset.MbtiBadge.isfp_inactive)
"INFP" -> painterResource(id = FunchIconAsset.MbtiBadge.infp_inactive)
"INTP" -> painterResource(id = FunchIconAsset.MbtiBadge.intp_inactive)
"ESTP" -> painterResource(id = FunchIconAsset.MbtiBadge.estp_inactive)
"ESFP" -> painterResource(id = FunchIconAsset.MbtiBadge.esfp_inactive)
"ENFP" -> painterResource(id = FunchIconAsset.MbtiBadge.enfp_inactive)
"ENTP" -> painterResource(id = FunchIconAsset.MbtiBadge.entp_inactive)
"ESTJ" -> painterResource(id = FunchIconAsset.MbtiBadge.estj_inactive)
"ESFJ" -> painterResource(id = FunchIconAsset.MbtiBadge.esfj_inactive)
"ENFJ" -> painterResource(id = FunchIconAsset.MbtiBadge.enfj_inactive)
"ENTJ" -> painterResource(id = FunchIconAsset.MbtiBadge.entj_inactive)
else -> throw IllegalArgumentException("Unknown Icon: $value")
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ object FunchIconAsset {
val profile_80 = R.drawable.ic_profile_80
val view_count_80 = R.drawable.ic_view_count_80
val code_80 = R.drawable.ic_code_80
val trophy_40 = R.drawable.ic_trophy_40
}

object Job {
Expand Down Expand Up @@ -89,4 +90,42 @@ object FunchIconAsset {
val eighty = R.drawable.ic_match_percentage80_120
val hundred = R.drawable.ic_match_percentage100_120
}

object MbtiBadge {
/* active */
val infj_active = R.drawable.ic_mbti_infj_active_60
val intj_active = R.drawable.ic_mbti_intj_active_60
val infp_active = R.drawable.ic_mbti_infp_active_60
val intp_active = R.drawable.ic_mbti_intp_active_60
val isfj_active = R.drawable.ic_mbti_isfj_active_60
val istj_active = R.drawable.ic_mbti_istj_active_60
val isfp_active = R.drawable.ic_mbti_isfp_active_60
val istp_active = R.drawable.ic_mbti_istp_active_60
val entj_active = R.drawable.ic_mbti_entj_active_60
val enfp_active = R.drawable.ic_mbti_enfp_active_60
val enfj_active = R.drawable.ic_mbti_enfj_active_60
val entp_active = R.drawable.ic_mbti_entp_active_60
val esfj_active = R.drawable.ic_mbti_esfj_active_60
val esfp_active = R.drawable.ic_mbti_esfp_active_60
val estj_active = R.drawable.ic_mbti_estj_active_60
val estp_active = R.drawable.ic_mbti_estp_active_60

/* inactive */
val infj_inactive = R.drawable.ic_mbti_infj_inactive_60
val intj_inactive = R.drawable.ic_mbti_intj_inactive_60
val infp_inactive = R.drawable.ic_mbti_infp_inactive_60
val intp_inactive = R.drawable.ic_mbti_intp_inactive_60
val isfj_inactive = R.drawable.ic_mbti_isfj_inactive_60
val istj_inactive = R.drawable.ic_mbti_istj_inactive_60
val isfp_inactive = R.drawable.ic_mbti_isfp_inactive_60
val istp_inactive = R.drawable.ic_mbti_istp_inactive_60
val entj_inactive = R.drawable.ic_mbti_entj_inactive_60
val enfp_inactive = R.drawable.ic_mbti_enfp_inactive_60
val enfj_inactive = R.drawable.ic_mbti_enfj_inactive_60
val entp_inactive = R.drawable.ic_mbti_entp_inactive_60
val esfj_inactive = R.drawable.ic_mbti_esfj_inactive_60
val esfp_inactive = R.drawable.ic_mbti_esfp_inactive_60
val estj_inactive = R.drawable.ic_mbti_estj_inactive_60
val estp_inactive = R.drawable.ic_mbti_estp_inactive_60
}
}
25 changes: 25 additions & 0 deletions core/designsystem/src/main/res/drawable/ic_mbti_enfj_active_60.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="60dp"
android:height="60dp"
android:viewportWidth="60"
android:viewportHeight="60">
<group>
<clip-path
android:pathData="M0,0h60v60h-60z"/>
<path
android:pathData="M54.492,18.684C54.049,18.312 53.27,17.851 52.102,17.851H49.569C48.843,17.851 48.276,18.028 47.833,18.259C47.391,18.028 46.824,17.851 46.098,17.851H38.589C37.704,17.851 36.96,17.975 36.305,18.205C35.897,18.365 35.543,18.56 35.207,18.79C35.171,18.754 35.136,18.719 35.1,18.684C34.658,18.312 33.878,17.851 32.727,17.851H30.372C29.221,17.851 28.459,18.294 27.981,18.684C27.839,18.79 27.68,18.949 27.521,19.162C27.432,19.056 27.326,18.967 27.237,18.878C26.724,18.418 25.803,17.869 24.386,17.869H20.897C20.065,17.869 19.427,18.064 18.931,18.329C18.471,18.064 17.869,17.851 17.054,17.851H9.545C8.66,17.851 7.916,17.975 7.261,18.205C6.411,18.524 5.72,19.02 5.207,19.711C4.728,20.331 4.445,21.057 4.321,21.871C4.25,22.296 4.233,22.774 4.233,23.253V34.746C4.233,35.224 4.268,35.702 4.321,36.127C4.445,36.942 4.746,37.668 5.207,38.288C5.72,38.961 6.411,39.475 7.261,39.793C7.916,40.041 8.66,40.148 9.545,40.148H17.054C17.798,40.148 18.382,39.953 18.825,39.705C19.268,39.953 19.87,40.165 20.649,40.165H23.005C24.156,40.165 24.917,39.723 25.396,39.333C25.555,39.209 25.732,39.014 25.909,38.784C26.033,38.926 26.157,39.05 26.281,39.156C26.795,39.616 27.715,40.165 29.132,40.165H32.462C33.471,40.165 34.215,39.864 34.746,39.51C35.207,39.829 35.897,40.148 36.871,40.148H39.368C40.519,40.148 41.281,39.705 41.759,39.315C42.22,38.926 42.981,38.058 42.981,36.553V33.825H45.283C45.089,34.02 44.911,34.233 44.752,34.48C44.416,35.047 44.238,35.685 44.238,36.411V38.678C44.238,39.882 44.787,40.944 45.744,41.582C46.328,41.954 47.001,42.149 47.763,42.149H47.816C48.861,42.149 49.764,42.025 50.596,41.83C51.659,41.582 52.58,41.122 53.359,40.502C54.191,39.811 54.829,38.908 55.218,37.845C55.537,36.942 55.696,35.933 55.696,34.729V21.464C55.696,19.941 54.917,19.056 54.457,18.684H54.492Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M10.661,27.627H16.417C16.576,27.627 16.7,27.662 16.771,27.715C16.842,27.769 16.877,27.893 16.877,28.07V30.212C16.877,30.39 16.842,30.514 16.771,30.567C16.7,30.62 16.576,30.655 16.417,30.655H10.661V33.064C10.661,33.4 10.714,33.648 10.838,33.772C10.962,33.896 11.175,33.967 11.493,33.967H17.09C17.249,33.967 17.373,34.002 17.444,34.056C17.515,34.109 17.55,34.233 17.55,34.41V36.553C17.55,36.73 17.515,36.854 17.444,36.907C17.373,36.96 17.249,36.995 17.09,36.995H9.581C9.085,36.995 8.695,36.942 8.412,36.836C8.129,36.73 7.916,36.57 7.757,36.375C7.615,36.181 7.509,35.95 7.473,35.667C7.438,35.384 7.403,35.083 7.403,34.746V23.253C7.403,22.916 7.42,22.615 7.473,22.332C7.509,22.048 7.615,21.818 7.757,21.623C7.898,21.429 8.129,21.269 8.412,21.163C8.695,21.057 9.085,21.003 9.581,21.003H17.09C17.249,21.003 17.373,21.039 17.444,21.092C17.515,21.145 17.55,21.269 17.55,21.446V23.589C17.55,23.766 17.515,23.89 17.444,23.943C17.373,23.997 17.249,24.032 17.09,24.032H11.493C11.175,24.032 10.962,24.103 10.838,24.227C10.714,24.351 10.661,24.581 10.661,24.935V27.645V27.627Z"
android:fillColor="#F4B4AA"/>
<path
android:pathData="M23.766,25.059H23.5V36.57C23.5,36.747 23.465,36.871 23.394,36.924C23.323,36.978 23.199,37.013 23.04,37.013H20.685C20.525,37.013 20.401,36.978 20.33,36.924C20.26,36.871 20.224,36.747 20.224,36.57V21.854C20.224,21.553 20.26,21.34 20.348,21.216C20.419,21.092 20.614,21.039 20.933,21.039H24.421C24.758,21.039 25.024,21.11 25.183,21.251C25.342,21.393 25.466,21.588 25.573,21.836L29.681,32.993H29.947V21.482C29.947,21.305 29.982,21.181 30.053,21.128C30.124,21.074 30.248,21.039 30.407,21.039H32.763C32.922,21.039 33.046,21.074 33.117,21.128C33.188,21.181 33.223,21.305 33.223,21.482V36.198C33.223,36.5 33.188,36.73 33.099,36.836C33.028,36.96 32.833,37.013 32.515,37.013H29.185C28.849,37.013 28.583,36.942 28.424,36.8C28.264,36.659 28.14,36.464 28.034,36.216L23.766,25.059Z"
android:fillColor="#F4B4AA"/>
<path
android:pathData="M39.882,27.627H45.46C45.62,27.627 45.744,27.662 45.815,27.715C45.886,27.769 45.921,27.893 45.921,28.07V30.212C45.921,30.39 45.886,30.514 45.815,30.567C45.744,30.62 45.62,30.655 45.46,30.655H39.882V36.535C39.882,36.712 39.847,36.836 39.776,36.889C39.705,36.942 39.581,36.978 39.422,36.978H36.924C36.765,36.978 36.641,36.942 36.57,36.889C36.499,36.836 36.464,36.712 36.464,36.535V23.235C36.464,22.899 36.482,22.597 36.535,22.314C36.57,22.031 36.676,21.801 36.818,21.606C36.96,21.411 37.19,21.251 37.473,21.145C37.757,21.039 38.146,20.986 38.642,20.986H46.151C46.311,20.986 46.435,21.021 46.505,21.074C46.576,21.128 46.612,21.251 46.612,21.429V23.571C46.612,23.749 46.576,23.872 46.505,23.926C46.435,23.979 46.311,24.014 46.151,24.014H40.732C40.413,24.014 40.183,24.085 40.077,24.209C39.971,24.333 39.9,24.563 39.9,24.917V27.627H39.882Z"
android:fillColor="#F4B4AA"/>
<path
android:pathData="M52.58,34.711C52.58,35.525 52.473,36.216 52.279,36.747C52.084,37.279 51.783,37.721 51.393,38.04C51.004,38.359 50.508,38.607 49.905,38.749C49.303,38.89 48.613,38.961 47.816,38.979C47.674,38.979 47.568,38.961 47.515,38.926C47.462,38.89 47.426,38.802 47.426,38.66V36.393C47.426,36.269 47.444,36.163 47.479,36.11C47.515,36.057 47.603,36.021 47.763,36.004C48.648,35.95 49.091,35.454 49.091,34.534V21.446C49.091,21.269 49.126,21.145 49.197,21.092C49.268,21.039 49.392,21.003 49.551,21.003H52.084C52.243,21.003 52.367,21.039 52.438,21.092C52.509,21.145 52.544,21.269 52.544,21.446V34.711H52.58Z"
android:fillColor="#F4B4AA"/>
</group>
</vector>
Loading

0 comments on commit e659c76

Please sign in to comment.