diff --git a/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleDropDownMenu.kt b/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleDropDownMenu.kt index c20211a4..52b85458 100644 --- a/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleDropDownMenu.kt +++ b/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleDropDownMenu.kt @@ -22,9 +22,11 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp +import hous.release.android.R import hous.release.designsystem.theme.HousBlue import hous.release.designsystem.theme.HousG2 import hous.release.designsystem.theme.HousG6 @@ -61,7 +63,7 @@ fun MainRuleDropDownMenu( contentPadding = PaddingValues(start = DropdownMenuItemContentStartPadding) ) { Text( - text = "Rules 편집", + text = stringResource(id = R.string.our_rule_menu_edit_representation), style = HousTheme.typography.b2, color = HousBlue, modifier = Modifier.wrapContentSize() @@ -77,7 +79,7 @@ fun MainRuleDropDownMenu( contentPadding = PaddingValues(start = DropdownMenuItemContentStartPadding) ) { Text( - text = "가이드 다시보기", + text = stringResource(id = R.string.our_rule_menu_edit_guide), style = HousTheme.typography.b2, color = HousG6, modifier = Modifier.wrapContentSize() diff --git a/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleList.kt b/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleList.kt index 42433c55..64df68d2 100644 --- a/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleList.kt +++ b/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleList.kt @@ -17,7 +17,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import hous.release.android.R -import hous.release.designsystem.component.HousDash +import hous.release.designsystem.component.HousDot import hous.release.designsystem.component.HousRuleSlot import hous.release.designsystem.theme.HousBlue import hous.release.designsystem.theme.HousG5 @@ -71,7 +71,7 @@ private fun MainRuleItem( text = mainRule.name, isShowTrailingIcon = mainRule.isNew, leadingIcon = { - HousDash(mainRule.isNew) + HousDot(mainRule.isNew) }, trailingIcon = { Text( diff --git a/app/src/main/java/hous/release/android/presentation/our_rules/graph/RuleNavGraph.kt b/app/src/main/java/hous/release/android/presentation/our_rules/graph/RuleNavGraph.kt index ab1c7d74..4761a714 100644 --- a/app/src/main/java/hous/release/android/presentation/our_rules/graph/RuleNavGraph.kt +++ b/app/src/main/java/hous/release/android/presentation/our_rules/graph/RuleNavGraph.kt @@ -46,9 +46,10 @@ private fun NavGraphBuilder.mainRuleScreen( val uiState = viewModel.uiState.collectAsStateWithLifecycle() MainRuleScreen( - detailRule = DetailRuleUiModel(), + detailRule = uiState.value.detailRule, mainRules = uiState.value.filteredRules, searchQuery = uiState.value.searchQuery, + fetchDetailRuleById = viewModel::fetchDetailRule, onSearch = viewModel::searchRule, onNavigateToAddRule = navController::navigateToAddRule, onNavigateToUpdateRule = navController::navigateUpdateRule, diff --git a/app/src/main/java/hous/release/android/presentation/our_rules/screen/MainRuleScreen.kt b/app/src/main/java/hous/release/android/presentation/our_rules/screen/MainRuleScreen.kt index 335f366c..19655f11 100644 --- a/app/src/main/java/hous/release/android/presentation/our_rules/screen/MainRuleScreen.kt +++ b/app/src/main/java/hous/release/android/presentation/our_rules/screen/MainRuleScreen.kt @@ -24,6 +24,7 @@ fun MainRuleScreen( detailRule: DetailRuleUiModel = DetailRuleUiModel(), mainRules: List = emptyList(), searchQuery: String = "", + fetchDetailRuleById: (Int) -> Unit = {}, onSearch: (String) -> Unit = {}, onNavigateToUpdateRule: (DetailRuleUiModel) -> Unit = {}, onNavigateToAddRule: () -> Unit = {}, @@ -63,8 +64,9 @@ fun MainRuleScreen( mainRules = mainRules, searchQuery = searchQuery, onSearch = onSearch, - onOpenDetailRule = { + onOpenDetailRule = { id -> coroutineScope.launch { + fetchDetailRuleById(id) bottomSheetState.show() } }, diff --git a/app/src/main/java/hous/release/android/presentation/our_rules/viewmodel/MainRuleViewModel.kt b/app/src/main/java/hous/release/android/presentation/our_rules/viewmodel/MainRuleViewModel.kt index 9741ba53..52db4b91 100644 --- a/app/src/main/java/hous/release/android/presentation/our_rules/viewmodel/MainRuleViewModel.kt +++ b/app/src/main/java/hous/release/android/presentation/our_rules/viewmodel/MainRuleViewModel.kt @@ -3,7 +3,10 @@ package hous.release.android.presentation.our_rules.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import hous.release.android.presentation.our_rules.model.DetailRuleUiModel +import hous.release.domain.entity.rule.DetailRule import hous.release.domain.entity.rule.MainRule +import hous.release.domain.usecase.rule.GetDetailRuleUseCase import hous.release.domain.usecase.rule.GetMainRulesUseCase import hous.release.domain.usecase.search.SearchRuleUseCase import kotlinx.coroutines.flow.MutableStateFlow @@ -16,6 +19,7 @@ import javax.inject.Inject @HiltViewModel class MainRuleViewModel @Inject constructor( private val getMainRulesUseCase: GetMainRulesUseCase, + private val getDetailRuleUseCase: GetDetailRuleUseCase, private val searcher: SearchRuleUseCase ) : ViewModel() { @@ -26,6 +30,20 @@ class MainRuleViewModel @Inject constructor( fetchMainRules() } + fun fetchDetailRule(id: Int) { + viewModelScope.launch { + runCatching { getDetailRuleUseCase(id) } + .onSuccess { detailRule -> + _uiState.update { state -> + state.copy(detailRule = detailRule.toUiModel()) + } + } + .onFailure { + Timber.e(it.stackTraceToString()) + } + } + } + fun fetchMainRules() { viewModelScope.launch { runCatching { getMainRulesUseCase() } @@ -51,9 +69,18 @@ class MainRuleViewModel @Inject constructor( ) } } + + private fun DetailRule.toUiModel() = DetailRuleUiModel( + id = id, + name = name, + description = description, + images = images, + updatedAt = updatedAt + ) } data class MainRules( + val detailRule: DetailRuleUiModel = DetailRuleUiModel(), val originRules: List = emptyList(), val filteredRules: List = emptyList(), val searchQuery: String = "" diff --git a/app/src/main/res/layout/fragment_hous.xml b/app/src/main/res/layout/fragment_hous.xml index d78d0736..7b8e17af 100644 --- a/app/src/main/res/layout/fragment_hous.xml +++ b/app/src/main/res/layout/fragment_hous.xml @@ -319,7 +319,7 @@ android:fontFamily="@font/montserrat_semibold" android:letterSpacing="-0.02" android:lineSpacingExtra="8dp" - android:text="@string/hous_our_rules_num_1" + android:text="@string/our_house_representative_rules_number_one" android:textColor="@color/hous_blue" android:textSize="16dp" android:textStyle="normal" @@ -360,7 +360,7 @@ android:fontFamily="@font/montserrat_semibold" android:letterSpacing="-0.02" android:lineSpacingExtra="8dp" - android:text="@string/hous_our_rules_num_2" + android:text="@string/our_house_representative_rules_number_two" android:textColor="@color/hous_blue" android:textSize="16dp" android:textStyle="normal" @@ -401,7 +401,7 @@ android:fontFamily="@font/montserrat_semibold" android:letterSpacing="-0.02" android:lineSpacingExtra="8dp" - android:text="@string/hous_our_rules_num_3" + android:text="@string/our_house_representative_rules_number_three" android:textColor="@color/hous_blue" android:textSize="16dp" android:textStyle="normal" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 977acd70..a9f19bf9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -100,7 +100,27 @@ 1. 2. 3. - + Our Rules + btn_of_our_rules + 아직 우리 집 Rules가 없어요! + 다른 Rule도 추가해보세요! + 똑같은 이름의 Rule이 있어요! + 아직 우리 집 Rules가 없어요! + 새로운 Rules 추가 + Rules 수정 + 저장 + 삭제 + Rules 삭제 + Rules 입력 + Rules 개수 초과 + 우리 집 Rules가 너무 많아요!\n필요하지 않은 Rule은 삭제하고\n다시 시도해주세요~ + 알겠어요! + %d/20 + 추가하기 + 삭제하기 + 수정하기 + Rules 편집 + 가이드 다시보기 설정 버튼 이제 하우스에 입장해볼까요? @@ -138,17 +158,10 @@ MY to-do img of myTod in hous 동글이의\n응원을 받고\n오늘도 화이팅! - Our Rules - btn_of_our_rules - 1. - 2. - 3. Homies img of profile color img of profile color - 아직 내 to-do가 없어요! - 아직 우리 집 Rules가 없어요! - 다른 Rule도 추가해보세요! + 아직 내 to-do가 없어요!> 초대코드가 복사되었습니다. 아직 성향 테스트를 하지 않은 호미예요! @@ -171,9 +184,6 @@ 완료되지 않음 1명 이상 완료 모두 완료 - 추가하기 - 삭제하기 - 수정하기 멤버별 보기 요일별 보기 @@ -186,19 +196,6 @@ + - - 똑같은 이름의 Rule이 있어요! - 아직 우리 집 Rules가 없어요! - 새로운 Rules 추가 - Rules 수정 - 저장 - 삭제 - Rules 삭제 - Rules 입력 - Rules 개수 초과 - 우리 집 Rules가 너무 많아요!\n필요하지 않은 Rule은 삭제하고\n다시 시도해주세요~ - 알겠어요! - %d/20 삭제하기 수정하기 안녕, to-do... diff --git a/data/src/main/java/hous/release/data/datasource/RuleDataSource.kt b/data/src/main/java/hous/release/data/datasource/RuleDataSource.kt index 6f017d71..e0cc10f1 100644 --- a/data/src/main/java/hous/release/data/datasource/RuleDataSource.kt +++ b/data/src/main/java/hous/release/data/datasource/RuleDataSource.kt @@ -4,6 +4,7 @@ import hous.release.data.entity.request.AddRulesRequest import hous.release.data.entity.request.DeleteRulesRequest import hous.release.data.entity.request.EditRulesRequest import hous.release.data.entity.response.NoDataResponse +import hous.release.data.entity.response.rule.DetailRuleResponse import hous.release.data.entity.response.rule.MainRuleResponse import hous.release.data.entity.response.rule.MainRulesResponse import hous.release.data.service.RuleService @@ -15,6 +16,8 @@ class RuleDataSource @Inject constructor(private val ruleService: RuleService) { suspend fun fetchMainRules(): MainRulesResponse = ruleService.getMainRules().data + suspend fun fetchDetailRuleBy(id: Int): DetailRuleResponse = + ruleService.getDetailRuleBy(id).data suspend fun postAddedRuleContent(addedRules: List): Result = runCatching { ruleService.postAddedRuleContent(AddRulesRequest(addedRules = addedRules)) } diff --git a/data/src/main/java/hous/release/data/entity/response/rule/DetailRuleResponse.kt b/data/src/main/java/hous/release/data/entity/response/rule/DetailRuleResponse.kt new file mode 100644 index 00000000..aa666b3e --- /dev/null +++ b/data/src/main/java/hous/release/data/entity/response/rule/DetailRuleResponse.kt @@ -0,0 +1,19 @@ +package hous.release.data.entity.response.rule + +import hous.release.domain.entity.rule.DetailRule + +data class DetailRuleResponse( + val id: Int = -1, + val name: String = "", + val description: String? = "", + val images: List = emptyList(), + val updatedAt: String = "" +) { + fun toDetailRule() = DetailRule( + id = id, + name = name, + description = description ?: "", + images = images, + updatedAt = updatedAt.substringBefore('T').replace('-', '.') + ) +} diff --git a/data/src/main/java/hous/release/data/repository/DefaultRuleRepository.kt b/data/src/main/java/hous/release/data/repository/DefaultRuleRepository.kt index 9921e43c..44d45d25 100644 --- a/data/src/main/java/hous/release/data/repository/DefaultRuleRepository.kt +++ b/data/src/main/java/hous/release/data/repository/DefaultRuleRepository.kt @@ -1,6 +1,7 @@ package hous.release.data.repository import hous.release.data.datasource.RuleDataSource +import hous.release.domain.entity.rule.DetailRule import hous.release.domain.entity.rule.MainRule import hous.release.domain.repository.RuleRepository import hous.release.domain.util.ApiResult @@ -20,6 +21,9 @@ class DefaultRuleRepository @Inject constructor( override suspend fun fetchMainRules(): List = ruleDataSource.fetchMainRules().rules.map { it.toMainRule() } + override suspend fun fetchDetailRule(id: Int): DetailRule = + ruleDataSource.fetchDetailRuleBy(id).toDetailRule() + override suspend fun postAddedRule(addedRuleList: List): Int { var code: Int = -999 ruleDataSource.postAddedRuleContent(addedRuleList).onSuccess { code = it.status } diff --git a/data/src/main/java/hous/release/data/service/RuleService.kt b/data/src/main/java/hous/release/data/service/RuleService.kt index c0c01833..c6ea552e 100644 --- a/data/src/main/java/hous/release/data/service/RuleService.kt +++ b/data/src/main/java/hous/release/data/service/RuleService.kt @@ -5,17 +5,22 @@ import hous.release.data.entity.request.DeleteRulesRequest import hous.release.data.entity.request.EditRulesRequest import hous.release.data.entity.response.BaseResponse import hous.release.data.entity.response.NoDataResponse +import hous.release.data.entity.response.rule.DetailRuleResponse import hous.release.data.entity.response.rule.MainRulesResponse import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.HTTP import retrofit2.http.POST import retrofit2.http.PUT +import retrofit2.http.Path interface RuleService { @GET("/v1/rules") suspend fun getMainRules(): BaseResponse + @GET("/v2/rule/{id}") + suspend fun getDetailRuleBy(@Path("id") id: Int): BaseResponse + @POST("/v1/rules") suspend fun postAddedRuleContent(@Body body: AddRulesRequest): NoDataResponse diff --git a/data/src/test/java/hous/release/data/service/RuleServiceTest.kt b/data/src/test/java/hous/release/data/service/RuleServiceTest.kt index 8ae0e78e..95bd21d3 100644 --- a/data/src/test/java/hous/release/data/service/RuleServiceTest.kt +++ b/data/src/test/java/hous/release/data/service/RuleServiceTest.kt @@ -3,6 +3,7 @@ package hous.release.data.service import com.google.common.truth.Truth.assertThat import com.google.gson.GsonBuilder import hous.release.data.entity.response.BaseResponse +import hous.release.data.entity.response.rule.DetailRuleResponse import hous.release.data.entity.response.rule.MainRuleResponse import hous.release.data.entity.response.rule.MainRulesResponse import io.mockk.junit5.MockKExtension @@ -64,4 +65,28 @@ internal class RuleServiceTest { // then assertThat(actualResponse).isEqualTo(expectedResponse) } + + @Test + fun `rule의 id를 통해 rule의 상세 내용을 불러올 수 있다`() = runTest { + // given + val detailRule = File("src/test/res/rule/success_detail_rule.json").readText() + val fakeResponse = MockResponse().setBody(detailRule).setResponseCode(200) + mockWebServer.enqueue(fakeResponse) + val expectedResponse = BaseResponse( + status = 200, + success = true, + message = "규칙 조회 성공입니다.", + data = DetailRuleResponse( + id = 34, + name = "이준원", + description = "ㅎㅇㅎㅇ", + images = listOf(), + updatedAt = "2023-03-16T17:19:42.158498" + ) + ) + // when + val actualResponse = ruleService.getDetailRuleBy(34) + // then + assertThat(actualResponse).isEqualTo(expectedResponse) + } } diff --git a/data/src/test/res/rule/success_detail_rule.json b/data/src/test/res/rule/success_detail_rule.json new file mode 100644 index 00000000..ca2b61b1 --- /dev/null +++ b/data/src/test/res/rule/success_detail_rule.json @@ -0,0 +1,12 @@ +{ + "status": 200, + "success": true, + "message": "규칙 조회 성공입니다.", + "data": { + "id": 34, + "description": "ㅎㅇㅎㅇ", + "images": [], + "name": "이준원", + "updatedAt": "2023-03-16T17:19:42.158498" + } +} diff --git a/designsystem/src/main/java/hous/release/designsystem/component/HousDash.kt b/designsystem/src/main/java/hous/release/designsystem/component/HousDot.kt similarity index 94% rename from designsystem/src/main/java/hous/release/designsystem/component/HousDash.kt rename to designsystem/src/main/java/hous/release/designsystem/component/HousDot.kt index fbdd4ad0..8db855bb 100644 --- a/designsystem/src/main/java/hous/release/designsystem/component/HousDash.kt +++ b/designsystem/src/main/java/hous/release/designsystem/component/HousDot.kt @@ -15,7 +15,7 @@ import hous.release.designsystem.theme.HousG3 import hous.release.designsystem.theme.HousTheme @Composable -fun HousDash( +fun HousDot( isNew: Boolean = false ) { Box( @@ -31,7 +31,7 @@ fun HousDash( @Composable private fun HousDashPreview() { HousTheme { - HousDash(isNew = true) + HousDot(isNew = true) } } @@ -39,6 +39,6 @@ private fun HousDashPreview() { @Composable private fun HousDashPreview2() { HousTheme { - HousDash() + HousDot() } } diff --git a/designsystem/src/main/java/hous/release/designsystem/component/HousRule.kt b/designsystem/src/main/java/hous/release/designsystem/component/HousRule.kt index 4df9895f..ad0ad9d8 100644 --- a/designsystem/src/main/java/hous/release/designsystem/component/HousRule.kt +++ b/designsystem/src/main/java/hous/release/designsystem/component/HousRule.kt @@ -59,7 +59,7 @@ fun HousEditRulePreview() { text = "text", isShowTrailingIcon = false, leadingIcon = { - HousDash(true) + HousDot(true) }, trailingIcon = { Text( @@ -84,7 +84,7 @@ fun HousRulePreview() { text = "text", isShowTrailingIcon = false, leadingIcon = { - HousDash() + HousDot() }, trailingIcon = { Text( diff --git a/domain/src/main/java/hous/release/domain/repository/RuleRepository.kt b/domain/src/main/java/hous/release/domain/repository/RuleRepository.kt index a9f01751..ba4143d1 100644 --- a/domain/src/main/java/hous/release/domain/repository/RuleRepository.kt +++ b/domain/src/main/java/hous/release/domain/repository/RuleRepository.kt @@ -1,11 +1,13 @@ package hous.release.domain.repository +import hous.release.domain.entity.rule.DetailRule import hous.release.domain.entity.rule.MainRule import hous.release.domain.util.ApiResult import kotlinx.coroutines.flow.Flow interface RuleRepository { suspend fun fetchMainRules(): List + suspend fun fetchDetailRule(id: Int): DetailRule suspend fun postAddedRule(addedRuleList: List): Int fun putEditedRuleContent(editedRuleList: List): Flow> fun deleteRuleContent(deleteRules: List): Flow> diff --git a/domain/src/main/java/hous/release/domain/usecase/rule/GetDetailRuleUseCase.kt b/domain/src/main/java/hous/release/domain/usecase/rule/GetDetailRuleUseCase.kt new file mode 100644 index 00000000..cc504467 --- /dev/null +++ b/domain/src/main/java/hous/release/domain/usecase/rule/GetDetailRuleUseCase.kt @@ -0,0 +1,10 @@ +package hous.release.domain.usecase.rule + +import hous.release.domain.entity.rule.DetailRule +import hous.release.domain.repository.RuleRepository +import javax.inject.Inject + +class GetDetailRuleUseCase @Inject constructor(private val repository: RuleRepository) { + + suspend operator fun invoke(id: Int): DetailRule = repository.fetchDetailRule(id) +}