diff --git a/app/src/main/java/hous/release/android/di/ReducerModule.kt b/app/src/main/java/hous/release/android/di/ReducerModule.kt index bdefa4614..f2ba9c99b 100644 --- a/app/src/main/java/hous/release/android/di/ReducerModule.kt +++ b/app/src/main/java/hous/release/android/di/ReducerModule.kt @@ -6,10 +6,13 @@ import dagger.hilt.InstallIn import dagger.hilt.android.components.ActivityRetainedComponent import hous.release.android.presentation.our_rules.event.AddRuleReducer import hous.release.android.presentation.our_rules.event.MainRuleReducer +import hous.release.android.presentation.our_rules.event.UpdateRuleReducer import hous.release.android.presentation.our_rules.viewmodel.AddRuleEvent import hous.release.android.presentation.our_rules.viewmodel.AddRuleState import hous.release.android.presentation.our_rules.viewmodel.MainRulesEvent import hous.release.android.presentation.our_rules.viewmodel.MainRulesState +import hous.release.android.presentation.our_rules.viewmodel.UpdateRuleEvent +import hous.release.android.presentation.our_rules.viewmodel.UpdateRuleState import hous.release.android.util.event.Reducer import javax.inject.Qualifier @@ -23,6 +26,10 @@ abstract class ReducerModule { @AddRule @Binds abstract fun bindAddRuleReducer(addRuleReducer: AddRuleReducer): Reducer + + @UpdateRule + @Binds + abstract fun bindUpdateRuleReducer(updateRuleReducer: UpdateRuleReducer): Reducer } @Retention(AnnotationRetention.BINARY) @@ -32,3 +39,7 @@ annotation class MainRules @Retention(AnnotationRetention.BINARY) @Qualifier annotation class AddRule + +@Retention(AnnotationRetention.BINARY) +@Qualifier +annotation class UpdateRule diff --git a/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleContent.kt b/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleContent.kt index 9103f18c7..f3e78e9b9 100644 --- a/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleContent.kt +++ b/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleContent.kt @@ -30,6 +30,7 @@ fun MainRuleContent( onSearch: (String) -> Unit = {}, onOpenDetailRule: (Int) -> Unit = {}, onNavigateToAddRule: () -> Unit = {}, + onNavigateToRepresentRule: () -> Unit = {}, onFinish: () -> Unit = {} ) { val focusManager = LocalFocusManager.current @@ -42,7 +43,8 @@ fun MainRuleContent( .padding( start = 16.dp, end = 16.dp - ).pointerInput(Unit) { + ) + .pointerInput(Unit) { detectTapGestures( onTap = { focusManager.clearFocus() @@ -52,7 +54,8 @@ fun MainRuleContent( horizontalAlignment = Alignment.CenterHorizontally ) { MainRuleToolbar( - onBack = onFinish + onBack = onFinish, + onNavigateToRepresentRule = onNavigateToRepresentRule ) Spacer(modifier = Modifier.padding(top = 4.dp)) HousTextField( diff --git a/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleToolbar.kt b/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleToolbar.kt index c4f7d8459..364aa24f1 100644 --- a/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleToolbar.kt +++ b/app/src/main/java/hous/release/android/presentation/our_rules/component/main/MainRuleToolbar.kt @@ -24,7 +24,7 @@ import hous.release.designsystem.theme.HousTheme @Composable fun MainRuleToolbar( - onClickSetting: () -> Unit = {}, + onNavigateToRepresentRule: () -> Unit = {}, modifier: Modifier = Modifier, title: String = "우리 집 Rules", onBack: () -> Unit = { } @@ -62,7 +62,7 @@ fun MainRuleToolbar( ) } ) - MainRuleDropDownMenu(expanded, onDismiss) + MainRuleDropDownMenu(expanded, onDismiss, onNavigateToRepresentRule) } } diff --git a/app/src/main/java/hous/release/android/presentation/our_rules/component/update/BasicUpdateRuleScreen.kt b/app/src/main/java/hous/release/android/presentation/our_rules/component/update/BasicUpdateRuleScreen.kt index 858c00fc8..6efbeedfa 100644 --- a/app/src/main/java/hous/release/android/presentation/our_rules/component/update/BasicUpdateRuleScreen.kt +++ b/app/src/main/java/hous/release/android/presentation/our_rules/component/update/BasicUpdateRuleScreen.kt @@ -40,6 +40,8 @@ fun BasicUpdateRuleScreen( ruleName: String = "", description: String = "", photos: List = emptyList(), + isFocusOn: Boolean = false, + onDisposeFocus: () -> Unit = {}, addRule: () -> Unit = { }, changeName: (String) -> Unit = {}, changeDescription: (String) -> Unit = {}, @@ -49,7 +51,12 @@ fun BasicUpdateRuleScreen( ) { val focusManager = LocalFocusManager.current val (focusRequester) = FocusRequester.createRefs() - AutoFocusOnInitializeEffect { focusRequester.requestFocus() } + AutoFocusOnInitializeEffect { + if (isFocusOn) { + focusRequester.requestFocus() + onDisposeFocus() + } + } Column( modifier = Modifier.fillMaxSize().pointerInput(Unit) { diff --git a/app/src/main/java/hous/release/android/presentation/our_rules/event/UpdateRuleReducer.kt b/app/src/main/java/hous/release/android/presentation/our_rules/event/UpdateRuleReducer.kt new file mode 100644 index 000000000..5c162656c --- /dev/null +++ b/app/src/main/java/hous/release/android/presentation/our_rules/event/UpdateRuleReducer.kt @@ -0,0 +1,51 @@ +package hous.release.android.presentation.our_rules.event + +import hous.release.android.presentation.our_rules.model.PhotoUiModel +import hous.release.android.presentation.our_rules.viewmodel.UpdateRuleEvent +import hous.release.android.presentation.our_rules.viewmodel.UpdateRuleState +import hous.release.android.util.event.Reducer +import timber.log.Timber +import javax.inject.Inject + +class UpdateRuleReducer @Inject constructor() : Reducer { + override fun dispatch(state: UpdateRuleState, event: UpdateRuleEvent): UpdateRuleState { + return when (event) { + is UpdateRuleEvent.InitRule -> { + state.copy( + id = event.id, + name = event.name, + description = event.description, + photos = event.photos + ) + } + + is UpdateRuleEvent.ChangeDescription -> { + state.copy(description = event.description) + } + + is UpdateRuleEvent.ChangeName -> { + state.copy(name = event.name) + } + + is UpdateRuleEvent.LoadImage -> { + val newPhotos = event.photos.map { photo -> + PhotoUiModel.from(photo) + } + val updatedPhotos = state.photos + newPhotos + Timber.e("LoadImage: updatedPhotos: $updatedPhotos") + state.copy( + photos = updatedPhotos + ) + } + + is UpdateRuleEvent.DeleteImage -> { + val filteredPhotos = state.photos.filter { photo -> + photo != event.photo + } + state.copy( + photos = filteredPhotos + ) + } + } + } +} 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 197c175f2..41a598886 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 @@ -23,6 +23,7 @@ import hous.release.android.R import hous.release.android.presentation.our_rules.component.LoadingBar import hous.release.android.presentation.our_rules.component.dialog.AddRuleLimitedDialog import hous.release.android.presentation.our_rules.component.dialog.AddRuleOutDialog +import hous.release.android.presentation.our_rules.component.dialog.UpdateRuleOutDialog import hous.release.android.presentation.our_rules.model.DetailRuleUiModel import hous.release.android.presentation.our_rules.screen.AddRuleScreen import hous.release.android.presentation.our_rules.screen.MainRuleScreen @@ -32,6 +33,8 @@ import hous.release.android.presentation.our_rules.viewmodel.AddRuleSideEffect import hous.release.android.presentation.our_rules.viewmodel.AddRuleViewModel import hous.release.android.presentation.our_rules.viewmodel.MainRuleSideEffect import hous.release.android.presentation.our_rules.viewmodel.MainRuleViewModel +import hous.release.android.presentation.our_rules.viewmodel.UpdateRuleSideEffect +import hous.release.android.presentation.our_rules.viewmodel.UpdateRuleViewModel import hous.release.android.presentation.practice.findActivity import hous.release.android.util.ToastMessageUtil import hous.release.domain.entity.Photo @@ -63,7 +66,7 @@ private fun NavGraphBuilder.mainRuleScreen( val activity = LocalContext.current.findActivity() val viewModel = hiltViewModel() val uiState = viewModel.uiState.collectAsStateWithLifecycle() - + var isLoading by remember { mutableStateOf(false) } var isShowLimitedAddRuleDialog by remember { mutableStateOf(false) } LaunchedEffect(Unit) { viewModel.sideEffect.collect { event -> @@ -76,6 +79,11 @@ private fun NavGraphBuilder.mainRuleScreen( isShowLimitedAddRuleDialog = true } } + + is MainRuleSideEffect.LoadingBar -> { + Timber.d("LoadingBar: ${event.isLoading}") + isLoading = event.isLoading + } } } } @@ -84,6 +92,7 @@ private fun NavGraphBuilder.mainRuleScreen( isShowLimitedAddRuleDialog = false }) } + if (isLoading) LoadingBar() MainRuleScreen( detailRule = uiState.value.detailRule, @@ -93,12 +102,15 @@ private fun NavGraphBuilder.mainRuleScreen( onSearch = viewModel::searchRule, onNavigateToAddRule = viewModel::canAddRule, onNavigateToUpdateRule = navController::navigateUpdateRule, + onNavigateToRepresentRule = navController::navigateToRepresentRule, onFinish = activity::finish, - refresh = viewModel::fetchMainRules + refresh = viewModel::fetchMainRules, + deleteRule = viewModel::deleteRule, ) } } +@OptIn(ExperimentalLifecycleComposeApi::class) private fun NavGraphBuilder.updateRuleScreen( navController: NavController ) { @@ -108,9 +120,90 @@ private fun NavGraphBuilder.updateRuleScreen( navController.previousBackStackEntry?.savedStateHandle?.get( RulesScreens.DETAIL_RULE_KEY )?.let { detailRule -> + val viewModel = hiltViewModel() + val uiState = viewModel.uiState.collectAsStateWithLifecycle() + var isOutDialogShow by remember { mutableStateOf(false) } + var isLoading by remember { mutableStateOf(false) } + val context = LocalContext.current + val takePhotoFromAlbumLauncher = rememberLauncherForActivityResult( + ActivityResultContracts.PickMultipleVisualMedia(5) + ) { uriList -> + if (uriList.isNotEmpty()) { + viewModel.loadImage(uriList.map { Photo.from(it.toString()) }) + } + } + val onBack: () -> Unit = { navController.popBackStack() } + val onOpenGallery = { + takePhotoFromAlbumLauncher.launch( + PickVisualMediaRequest( + ActivityResultContracts.PickVisualMedia.ImageOnly + ) + ) + } + LaunchedEffect(Unit) { + viewModel.init(detailRule) + } + LaunchedEffect(Unit) { + viewModel.sideEffect.collect { event -> + when (event) { + is UpdateRuleSideEffect.IDLE -> Unit + is UpdateRuleSideEffect.DuplicateToast -> { + ToastMessageUtil.showToast( + context, + context.getString(R.string.our_rule_duplicate_rule) + ) + } + + is UpdateRuleSideEffect.ShowLimitImageToast -> { + ToastMessageUtil.showToast( + context, + context.getString(R.string.our_rule_limit_photo_count) + ) + } + + is UpdateRuleSideEffect.LoadingBar -> { + isLoading = event.isLoading + } + + is UpdateRuleSideEffect.PopBackStack -> { + onBack() + } + } + } + } + + val onBackPressed = { + if (uiState.value.name.isNotBlank()) { + isOutDialogShow = true + } else { + Timber.d("onBackPressed") + onBack() + } + } + BackHandler(viewModel.isChangeRuleContent(detailRule), onBackPressed) + if (isLoading) LoadingBar() + + if (isOutDialogShow) { + UpdateRuleOutDialog( + onConfirm = { + isOutDialogShow = false + onBack() + }, + onDismiss = { + isOutDialogShow = false + } + ) + } UpdateRuleScreen( - rule = detailRule, - onBack = navController::popBackStack + ruleName = uiState.value.name, + description = uiState.value.description, + photos = uiState.value.photos, + changeName = viewModel::changeName, + changeDescription = viewModel::changeDescription, + updateRule = viewModel::updateRule, + deletePhoto = viewModel::deleteImage, + onBack = onBackPressed, + onOpenGallery = onOpenGallery ) } ?: run { Timber.e("DetailRuleUiModel is null") @@ -146,7 +239,6 @@ private fun NavGraphBuilder.addRuleScreen(onBack: () -> Unit) { when (event) { is AddRuleSideEffect.IDLE -> Unit is AddRuleSideEffect.DuplicateToast -> { - isLoading = false ToastMessageUtil.showToast( context, context.getString(R.string.our_rule_duplicate_rule) @@ -165,12 +257,10 @@ private fun NavGraphBuilder.addRuleScreen(onBack: () -> Unit) { } is AddRuleSideEffect.PopBackStack -> { - isLoading = false onBack() } is AddRuleSideEffect.ShowLimitRuleCountDialog -> { - isLoading = false isShowLimitedDialog = true } } @@ -220,7 +310,6 @@ private fun NavGraphBuilder.representativeRuleScreen(onBack: () -> Unit) { } } // Navigation - fun NavController.navigateToAddRule() { navigate(RulesScreens.Add.route) } @@ -232,3 +321,7 @@ fun NavController.navigateUpdateRule(detailRuleUiModel: DetailRuleUiModel) { ) navigate(RulesScreens.Update.route) } + +fun NavController.navigateToRepresentRule() { + navigate(RulesScreens.Represent.route) +} \ No newline at end of file diff --git a/app/src/main/java/hous/release/android/presentation/our_rules/screen/AddRuleScreen.kt b/app/src/main/java/hous/release/android/presentation/our_rules/screen/AddRuleScreen.kt index b94ae03c4..6d8d5036b 100644 --- a/app/src/main/java/hous/release/android/presentation/our_rules/screen/AddRuleScreen.kt +++ b/app/src/main/java/hous/release/android/presentation/our_rules/screen/AddRuleScreen.kt @@ -1,6 +1,10 @@ package hous.release.android.presentation.our_rules.screen import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import hous.release.android.R @@ -20,7 +24,11 @@ fun AddRuleScreen( onOpenGallery: () -> Unit = {}, onBack: () -> Unit = {} ) { + var isFocus by remember { mutableStateOf(true) } + val onDisposeFocus = { isFocus = false } BasicUpdateRuleScreen( + isFocusOn = isFocus, + onDisposeFocus = onDisposeFocus, title = stringResource(id = R.string.our_rule_add_new_rule_title), trailingTitle = stringResource(id = R.string.our_rule_add_new_rule), ruleName = ruleName, 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 d2d0b907e..24dc5a9d4 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 @@ -8,12 +8,17 @@ import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver +import hous.release.android.presentation.our_rules.component.dialog.DeleteRuleDialog import hous.release.android.presentation.our_rules.component.main.DetailRuleBottomSheetContent import hous.release.android.presentation.our_rules.component.main.MainRuleContent import hous.release.android.presentation.our_rules.model.DetailRuleUiModel @@ -29,9 +34,11 @@ fun MainRuleScreen( mainRules: List = emptyList(), searchQuery: String = "", fetchDetailRuleById: (Int) -> Unit = {}, + deleteRule: () -> Unit = {}, onSearch: (String) -> Unit = {}, onNavigateToUpdateRule: (DetailRuleUiModel) -> Unit = {}, onNavigateToAddRule: () -> Unit = {}, + onNavigateToRepresentRule: () -> Unit = {}, onFinish: () -> Unit = {}, refresh: () -> Unit = {} ) { @@ -40,6 +47,7 @@ fun MainRuleScreen( initialValue = ModalBottomSheetValue.Hidden, skipHalfExpanded = true ) + var isShowDeleteRuleDialog by remember { mutableStateOf(false) } val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner) { val observer = LifecycleEventObserver { _, event -> @@ -59,18 +67,31 @@ fun MainRuleScreen( bottomSheetState.hide() } } + if (isShowDeleteRuleDialog) { + DeleteRuleDialog( + onConfirm = { + coroutineScope.launch { + deleteRule() + isShowDeleteRuleDialog = false + bottomSheetState.hide() + } + }, + onDismiss = { isShowDeleteRuleDialog = false } + ) + } ModalBottomSheetLayout( sheetState = bottomSheetState, sheetContent = { DetailRuleBottomSheetContent( detailRule = detailRule, - onNavigateToUpdateRule = onNavigateToUpdateRule, - onDeleteRule = { + onNavigateToUpdateRule = { detailRule -> coroutineScope.launch { bottomSheetState.hide() + onNavigateToUpdateRule(detailRule) } - } + }, + onDeleteRule = { isShowDeleteRuleDialog = true } ) }, sheetShape = RoundedCornerShape(topStart = 10.dp, topEnd = 10.dp), @@ -88,6 +109,7 @@ fun MainRuleScreen( } }, onNavigateToAddRule = onNavigateToAddRule, + onNavigateToRepresentRule = onNavigateToRepresentRule, onFinish = onFinish ) } diff --git a/app/src/main/java/hous/release/android/presentation/our_rules/screen/UpdateRuleScreen.kt b/app/src/main/java/hous/release/android/presentation/our_rules/screen/UpdateRuleScreen.kt index 2056bce59..909c2b549 100644 --- a/app/src/main/java/hous/release/android/presentation/our_rules/screen/UpdateRuleScreen.kt +++ b/app/src/main/java/hous/release/android/presentation/our_rules/screen/UpdateRuleScreen.kt @@ -1,28 +1,44 @@ package hous.release.android.presentation.our_rules.screen -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import hous.release.android.presentation.our_rules.model.DetailRuleUiModel +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import hous.release.android.R +import hous.release.android.presentation.our_rules.component.update.BasicUpdateRuleScreen +import hous.release.android.presentation.our_rules.model.PhotoUiModel import hous.release.designsystem.theme.HousTheme @Composable fun UpdateRuleScreen( - onBack: () -> Boolean, - rule: DetailRuleUiModel + ruleName: String = "", + description: String = "", + photos: List = emptyList(), + updateRule: () -> Unit = { }, + changeName: (String) -> Unit = {}, + changeDescription: (String) -> Unit = {}, + deletePhoto: (photo: PhotoUiModel) -> Unit = {}, + onOpenGallery: () -> Unit = {}, + onBack: () -> Unit = {} ) { - Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - Text( - text = "Update Rule ${rule.id}", - style = HousTheme.typography.h1 - ) + BasicUpdateRuleScreen( + title = stringResource(id = R.string.our_rule_edit_rule_title), + trailingTitle = stringResource(id = R.string.our_rule_save_new_rule), + ruleName = ruleName, + description = description, + photos = photos, + addRule = updateRule, + changeName = changeName, + changeDescription = changeDescription, + deletePhoto = deletePhoto, + onOpenGallery = onOpenGallery, + onBack = onBack + ) +} + +@Preview(showBackground = true) +@Composable +private fun UpdateRuleScreenPreview() { + HousTheme { + UpdateRuleScreen() } } diff --git a/app/src/main/java/hous/release/android/presentation/our_rules/viewmodel/AddRuleViewModel.kt b/app/src/main/java/hous/release/android/presentation/our_rules/viewmodel/AddRuleViewModel.kt index ac96982e4..41e385cce 100644 --- a/app/src/main/java/hous/release/android/presentation/our_rules/viewmodel/AddRuleViewModel.kt +++ b/app/src/main/java/hous/release/android/presentation/our_rules/viewmodel/AddRuleViewModel.kt @@ -81,13 +81,16 @@ class AddRuleViewModel @Inject constructor( photoUris ) }.onSuccess { + _sideEffect.send(AddRuleSideEffect.LoadingBar(false)) _sideEffect.send(AddRuleSideEffect.PopBackStack) }.onFailure { e -> if (e is HttpException) { when (e.code()) { DUPLICATE_CODE -> _sideEffect.send(AddRuleSideEffect.DuplicateToast) LIMITED_RULE_COUNT_CODE -> _sideEffect.send(AddRuleSideEffect.ShowLimitRuleCountDialog) + else -> Timber.e("addRule() - onFailure() - e: ${e.stackTraceToString()}") } + _sideEffect.send(AddRuleSideEffect.LoadingBar(false)) } else { Timber.e("addRule() - onFailure() - e: ${e.stackTraceToString()}") _sideEffect.send(AddRuleSideEffect.LoadingBar(false)) 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 f274f8fde..b4986084b 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 @@ -11,12 +11,12 @@ import hous.release.domain.entity.rule.DetailRule import hous.release.domain.entity.rule.Rule import hous.release.domain.repository.PhotoRepository import hous.release.domain.usecase.rule.CanAddRuleUseCase +import hous.release.domain.usecase.rule.DeleteRuleUseCase import hous.release.domain.usecase.rule.GetDetailRuleUseCase import hous.release.domain.usecase.rule.GetRulesUseCase import hous.release.domain.usecase.search.SearchRuleUseCase import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.runningFold import kotlinx.coroutines.flow.stateIn @@ -30,6 +30,7 @@ class MainRuleViewModel @Inject constructor( private val getMainRulesUseCase: GetRulesUseCase, private val getDetailRuleUseCase: GetDetailRuleUseCase, private val canAddRuleUseCase: CanAddRuleUseCase, + private val deleteRuleUseCase: DeleteRuleUseCase, private val searcher: SearchRuleUseCase, @MainRules private val reducer: Reducer ) : ViewModel() { @@ -65,6 +66,23 @@ class MainRuleViewModel @Inject constructor( } } + fun deleteRule() { + viewModelScope.launch { + val id = uiState.value.detailRule.id + val paths = uiState.value.detailRule.photos.mapNotNull { it.filePath } + _sideEffect.send(MainRuleSideEffect.LoadingBar(true)) + runCatching { deleteRuleUseCase(id, paths) } + .onSuccess { + fetchMainRules() + _sideEffect.send(MainRuleSideEffect.LoadingBar(false)) + } + .onFailure { + Timber.e(it.stackTraceToString()) + _sideEffect.send(MainRuleSideEffect.LoadingBar(false)) + } + } + } + fun searchRule(searchQuery: String) { viewModelScope.launch { uiEvents.send( @@ -81,10 +99,9 @@ class MainRuleViewModel @Inject constructor( runCatching { getDetailRuleUseCase(id) }.onSuccess { _detailRule: DetailRule -> uiEvents.send(MainRulesEvent.FetchDetailRule(_detailRule)) // image Url을 photo Uri로 변환하는 작업 - photoSaver.fetchRemotePhotosFlow(_detailRule.images) - .distinctUntilChanged() + photoSaver.fetchPhotosFlow(_detailRule.images) .collect { - Timber.d("fetchRemotePhotosFlow: $it") + Timber.d("fetchPhotosFlow: $it") uiEvents.send(MainRulesEvent.LoadedImage(it)) } }.onFailure { @@ -96,6 +113,7 @@ class MainRuleViewModel @Inject constructor( sealed class MainRuleSideEffect { object IDLE : MainRuleSideEffect() + data class LoadingBar(val isLoading: Boolean) : MainRuleSideEffect() data class ShowLimitedAddRuleDialog(val isShow: Boolean) : MainRuleSideEffect() } diff --git a/app/src/main/java/hous/release/android/presentation/our_rules/viewmodel/UpdateRuleViewModel.kt b/app/src/main/java/hous/release/android/presentation/our_rules/viewmodel/UpdateRuleViewModel.kt new file mode 100644 index 000000000..092e907b7 --- /dev/null +++ b/app/src/main/java/hous/release/android/presentation/our_rules/viewmodel/UpdateRuleViewModel.kt @@ -0,0 +1,151 @@ +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.di.UpdateRule +import hous.release.android.presentation.our_rules.model.DetailRuleUiModel +import hous.release.android.presentation.our_rules.model.PhotoUiModel +import hous.release.android.util.event.Reducer +import hous.release.domain.entity.Photo +import hous.release.domain.usecase.rule.UpdateRuleUseCase +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.flow.runningFold +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import retrofit2.HttpException +import timber.log.Timber +import javax.inject.Inject + +@HiltViewModel +class UpdateRuleViewModel @Inject constructor( + private val updateRuleUseCase: UpdateRuleUseCase, + @UpdateRule private val reducer: Reducer +) : ViewModel() { + private val uiEvents = Channel() + val uiState = uiEvents + .receiveAsFlow() + .runningFold(UpdateRuleState(), reducer::dispatch) + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), UpdateRuleState()) + + private val _sideEffect: Channel = Channel() + val sideEffect = _sideEffect.receiveAsFlow() + + private fun canAddPhotos(photoSize: Int): Boolean { + return (uiState.value.photos.size + photoSize) <= 5 + } + + fun init(detailRule: DetailRuleUiModel) { + viewModelScope.launch { + uiEvents.send( + UpdateRuleEvent.InitRule( + detailRule.id, + detailRule.name, + detailRule.description, + detailRule.photos + ) + ) + } + } + + fun isChangeRuleContent(detailRule: DetailRuleUiModel) = uiState.value.run { + detailRule.name != name || detailRule.description != description || detailRule.photos != photos + } + + fun changeName(name: String) { + viewModelScope.launch { + uiEvents.send(UpdateRuleEvent.ChangeName(name)) + } + } + + fun changeDescription(description: String) { + viewModelScope.launch { + uiEvents.send(UpdateRuleEvent.ChangeDescription(description)) + } + } + + fun loadImage(photos: List) { + viewModelScope.launch { + if (canAddPhotos(photos.size)) { + Timber.e("loadImage() - photoUris: $photos") + uiEvents.send(UpdateRuleEvent.LoadImage(photos)) + return@launch + } + _sideEffect.send(UpdateRuleSideEffect.ShowLimitImageToast) + } + } + + fun deleteImage(photo: PhotoUiModel) { + viewModelScope.launch { + uiEvents.send(UpdateRuleEvent.DeleteImage(photo)) + } + } + + fun updateRule() { + viewModelScope.launch { + _sideEffect.send(UpdateRuleSideEffect.LoadingBar(true)) + runCatching { + val photoUris = uiState.value.photos.map { photo -> + photo.filePath?.let { path -> + Photo.from(path) + } ?: throw NullPointerException("filePath가 null이다") + } + updateRuleUseCase( + uiState.value.id, + uiState.value.description, + uiState.value.name, + photoUris + ) + }.onSuccess { + _sideEffect.send(UpdateRuleSideEffect.LoadingBar(false)) + _sideEffect.send(UpdateRuleSideEffect.PopBackStack) + }.onFailure { e -> + if (e is HttpException) { + when (e.code()) { + DUPLICATE_CODE -> _sideEffect.send(UpdateRuleSideEffect.DuplicateToast) + else -> Timber.e("addRule() - onFailure() - e: ${e.stackTraceToString()}") + } + _sideEffect.send(UpdateRuleSideEffect.LoadingBar(false)) + } else { + Timber.e("updateRule() - onFailure() - e: ${e.stackTraceToString()}") + _sideEffect.send(UpdateRuleSideEffect.LoadingBar(false)) + } + } + } + } + + private companion object { + const val DUPLICATE_CODE = 409 + } +} + +sealed class UpdateRuleSideEffect { + object IDLE : UpdateRuleSideEffect() + data class LoadingBar(val isLoading: Boolean) : UpdateRuleSideEffect() + object ShowLimitImageToast : UpdateRuleSideEffect() + object DuplicateToast : UpdateRuleSideEffect() + object PopBackStack : UpdateRuleSideEffect() +} + +data class UpdateRuleState( + val id: Int = 0, + val name: String = "", + val description: String = "", + val photos: List = emptyList() +) + +sealed class UpdateRuleEvent { + data class InitRule( + val id: Int, + val name: String, + val description: String, + val photos: List + ) : UpdateRuleEvent() + + data class ChangeDescription(val description: String) : UpdateRuleEvent() + data class ChangeName(val name: String) : UpdateRuleEvent() + data class LoadImage(val photos: List) : UpdateRuleEvent() + data class DeleteImage(val photo: PhotoUiModel) : UpdateRuleEvent() +} diff --git a/app/src/main/java/hous/release/android/presentation/settings/SettingsActivity.kt b/app/src/main/java/hous/release/android/presentation/settings/SettingsActivity.kt index 1e28b5ab9..300365eb1 100644 --- a/app/src/main/java/hous/release/android/presentation/settings/SettingsActivity.kt +++ b/app/src/main/java/hous/release/android/presentation/settings/SettingsActivity.kt @@ -6,7 +6,6 @@ import android.os.Build import android.os.Bundle import androidx.activity.viewModels import dagger.hilt.android.AndroidEntryPoint -import hous.release.android.BuildConfig import hous.release.android.R import hous.release.android.databinding.ActivitySettingsBinding import hous.release.android.presentation.login.LoginActivity @@ -73,22 +72,8 @@ class SettingsActivity : BindingActivity(R.layout.activ private fun initFeedbackClickListener() { binding.tvSettingsFeedback.setOnClickListener { startActivity( - Intent(Intent.ACTION_SEND).apply { - type = "plain/text" - putExtra( - Intent.EXTRA_EMAIL, - arrayOf(getString(R.string.settings_feedback_email)) - ) - putExtra(Intent.EXTRA_SUBJECT, getString(R.string.settings_feedback_subject)) - putExtra( - Intent.EXTRA_TEXT, - getString( - R.string.settings_feedback_content, - getDeviceName(), - Build.VERSION.SDK_INT.toString(), - BuildConfig.VERSION_NAME - ) - ) + Intent(this, WithdrawActivity::class.java).apply { + putExtra(IS_DELETING, false) } ) } @@ -102,7 +87,11 @@ class SettingsActivity : BindingActivity(R.layout.activ private fun initWithdrawClickListener() { binding.tvSettingsWithdraw.setOnClickListener { - startActivity(Intent(this, WithdrawActivity::class.java)) + startActivity( + Intent(this, WithdrawActivity::class.java).apply { + putExtra(IS_DELETING, true) + } + ) } } @@ -175,5 +164,6 @@ class SettingsActivity : BindingActivity(R.layout.activ companion object { const val HAS_ROOM = "HAS_ROOM" + const val IS_DELETING = "isDeleting" } } diff --git a/app/src/main/java/hous/release/android/presentation/withdraw/WithdrawActivity.kt b/app/src/main/java/hous/release/android/presentation/withdraw/WithdrawActivity.kt index 4d6f27a88..c45f283e4 100644 --- a/app/src/main/java/hous/release/android/presentation/withdraw/WithdrawActivity.kt +++ b/app/src/main/java/hous/release/android/presentation/withdraw/WithdrawActivity.kt @@ -1,109 +1,14 @@ package hous.release.android.presentation.withdraw -import android.content.Intent import android.os.Bundle -import android.view.View -import android.widget.AdapterView -import android.widget.ArrayAdapter -import androidx.activity.viewModels import dagger.hilt.android.AndroidEntryPoint import hous.release.android.R import hous.release.android.databinding.ActivityWithdrawBinding -import hous.release.android.presentation.login.LoginActivity -import hous.release.android.util.ToastMessageUtil -import hous.release.android.util.UiEvent import hous.release.android.util.binding.BindingActivity -import hous.release.android.util.dialog.LoadingDialogFragment -import hous.release.android.util.extension.repeatOnStarted -import hous.release.domain.entity.FeedbackType @AndroidEntryPoint -class WithdrawActivity : - BindingActivity(R.layout.activity_withdraw) { - private val viewModel by viewModels() - private val loadingDialog = LoadingDialogFragment() - +class WithdrawActivity : BindingActivity(R.layout.activity_withdraw) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding.vm = viewModel - initBackBtnClickListener() - initWithdrawUiEventCollector() - initWithdrawFeedbackSpinner() - } - - private fun initBackBtnClickListener() { - binding.btnWithdrawBack.setOnClickListener { finish() } - } - - private fun initWithdrawUiEventCollector() { - repeatOnStarted { - viewModel.withdrawUiEvent.collect { uiEvent -> - when (uiEvent) { - UiEvent.LOADING -> { - loadingDialog.show(supportFragmentManager, LoadingDialogFragment.TAG) - } - UiEvent.SUCCESS -> { - loadingDialog.dismiss() - ToastMessageUtil.showToast( - this@WithdrawActivity, - getString(R.string.withdraw_toast) - ) - startActivity( - Intent(this, LoginActivity::class.java).apply { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) - } - ) - } - UiEvent.ERROR -> { - loadingDialog.dismiss() - } - } - } - } - } - - private fun initWithdrawFeedbackSpinner() { - with(binding.spinnerWithdrawFeedbackSelect) { - adapter = ArrayAdapter( - this@WithdrawActivity, - R.layout.item_withdraw_feedback_title, - feedbackTypeItems - ).apply { - setDropDownViewResource(R.layout.item_withdraw_feedback) - } - onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onItemSelected( - adapterView: AdapterView<*>?, - view: View?, - position: Int, - id: Long - ) { - viewModel.initFeedbackType( - when (feedbackTypeItems[position]) { - FeedbackType.NO.type -> FeedbackType.NO - FeedbackType.DONE_LIVING_TOGETHER.type -> FeedbackType.DONE_LIVING_TOGETHER - FeedbackType.INCONVENIENT_TO_USE.type -> FeedbackType.INCONVENIENT_TO_USE - FeedbackType.LOW_USAGE.type -> FeedbackType.LOW_USAGE - FeedbackType.CONTENTS_UNSATISFACTORY.type -> FeedbackType.CONTENTS_UNSATISFACTORY - FeedbackType.ETC.type -> FeedbackType.ETC - else -> FeedbackType.NO - } - ) - } - - override fun onNothingSelected(adapterView: AdapterView<*>?) {} - } - } - } - - companion object { - private val feedbackTypeItems = listOf( - FeedbackType.NO.type, - FeedbackType.DONE_LIVING_TOGETHER.type, - FeedbackType.INCONVENIENT_TO_USE.type, - FeedbackType.LOW_USAGE.type, - FeedbackType.CONTENTS_UNSATISFACTORY.type, - FeedbackType.ETC.type - ) } } diff --git a/app/src/main/java/hous/release/android/presentation/withdraw/WithdrawViewModel.kt b/app/src/main/java/hous/release/android/presentation/withdraw/WithdrawViewModel.kt deleted file mode 100644 index 38471428d..000000000 --- a/app/src/main/java/hous/release/android/presentation/withdraw/WithdrawViewModel.kt +++ /dev/null @@ -1,48 +0,0 @@ -package hous.release.android.presentation.withdraw - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import dagger.hilt.android.lifecycle.HiltViewModel -import hous.release.android.util.UiEvent -import hous.release.domain.entity.FeedbackType -import hous.release.domain.repository.AuthRepository -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch -import timber.log.Timber -import javax.inject.Inject - -@HiltViewModel -class WithdrawViewModel @Inject constructor( - private val authRepository: AuthRepository -) : ViewModel() { - private var feedbackType = FeedbackType.NO - - val comment = MutableStateFlow("") - - val isCheckedWithdraw = MutableStateFlow(false) - - private val _withdrawUiEvent = MutableSharedFlow() - val withdrawUiEvent: SharedFlow = _withdrawUiEvent.asSharedFlow() - - fun initFeedbackType(type: FeedbackType) { - feedbackType = type - } - - fun deleteUser() { - viewModelScope.launch { - _withdrawUiEvent.emit(UiEvent.LOADING) - authRepository.deleteUser(feedbackType = feedbackType, comment = comment.value) - .onSuccess { - authRepository.clearLocalPref() - _withdrawUiEvent.emit(UiEvent.SUCCESS) - } - .onFailure { - Timber.d(it.message.toString()) - _withdrawUiEvent.emit(UiEvent.ERROR) - } - } - } -} diff --git a/app/src/main/java/hous/release/android/presentation/withdraw/feedback/FeedbackFragment.kt b/app/src/main/java/hous/release/android/presentation/withdraw/feedback/FeedbackFragment.kt new file mode 100644 index 000000000..6512b55a9 --- /dev/null +++ b/app/src/main/java/hous/release/android/presentation/withdraw/feedback/FeedbackFragment.kt @@ -0,0 +1,87 @@ +package hous.release.android.presentation.withdraw.feedback + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import dagger.hilt.android.AndroidEntryPoint +import hous.release.android.R +import hous.release.android.databinding.FragmentFeedbackBinding +import hous.release.android.presentation.settings.SettingsActivity +import hous.release.android.util.KeyBoardUtil +import hous.release.android.util.UiEvent +import hous.release.android.util.binding.BindingFragment +import hous.release.android.util.dialog.LoadingDialogFragment +import hous.release.android.util.extension.repeatOnStarted + +@AndroidEntryPoint +class FeedbackFragment : BindingFragment(R.layout.fragment_feedback) { + private val feedbackViewModel by viewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.viewModel = feedbackViewModel + initIsDeleting() + collectUiEvent() + collectIsSkip() + initEditTextClearFocus() + initBackBtnClickListener() + } + + private fun initIsDeleting() { + feedbackViewModel.initIsDeleting( + requireActivity().intent.getBooleanExtra(SettingsActivity.IS_DELETING, false) + ) + } + + @SuppressLint("ClickableViewAccessibility") + private fun initEditTextClearFocus() { + binding.clFeedback.setOnTouchListener { _, _ -> + KeyBoardUtil.hide(activity = requireActivity()) + return@setOnTouchListener false + } + } + + private fun initBackBtnClickListener() { + binding.btnFeedbackBack.setOnClickListener { + requireActivity().finish() + } + } + + private fun collectIsSkip() { + repeatOnStarted { + feedbackViewModel.isSkip.collect { skip -> + if (skip) { + findNavController().navigate(R.id.action_feedbackFragment_to_withdrawFragment) + } + } + } + } + + private fun collectUiEvent() { + repeatOnStarted { + val loadingDialogFragment = + childFragmentManager.findFragmentByTag(LoadingDialogFragment.TAG) as? LoadingDialogFragment + + feedbackViewModel.uiEvent.collect { uiEvent -> + when (uiEvent) { + UiEvent.LOADING -> { + loadingDialogFragment?.show(childFragmentManager, LoadingDialogFragment.TAG) + } + UiEvent.SUCCESS -> { + loadingDialogFragment?.dismiss() + if (feedbackViewModel.isDeleting.value) { + findNavController().navigate(R.id.action_feedbackFragment_to_withdrawFragment) + } else { + findNavController().navigate(R.id.action_feedbackFragment_to_withdrawDoneFragment) + } + } + UiEvent.ERROR -> { + loadingDialogFragment?.dismiss() + } + } + } + } + } +} diff --git a/app/src/main/java/hous/release/android/presentation/withdraw/feedback/FeedbackViewModel.kt b/app/src/main/java/hous/release/android/presentation/withdraw/feedback/FeedbackViewModel.kt new file mode 100644 index 000000000..12d031511 --- /dev/null +++ b/app/src/main/java/hous/release/android/presentation/withdraw/feedback/FeedbackViewModel.kt @@ -0,0 +1,61 @@ +package hous.release.android.presentation.withdraw.feedback + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import hous.release.android.util.UiEvent +import hous.release.domain.usecase.PostFeedbackUseCase +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject + +@HiltViewModel +class FeedbackViewModel @Inject constructor( + private val postFeedbackUseCase: PostFeedbackUseCase +) : ViewModel() { + private val _uiEvent = MutableSharedFlow() + val uiEvent: SharedFlow = _uiEvent.asSharedFlow() + + private val _isSkip = MutableSharedFlow() + val isSkip = _isSkip.asSharedFlow() + + private val _isDeleting = MutableStateFlow(false) + val isDeleting = _isDeleting.asStateFlow() + + val comment = MutableStateFlow("") + + private fun postFeedback() { + viewModelScope.launch { + _uiEvent.emit(UiEvent.LOADING) + postFeedbackUseCase(comment = comment.value, isDeleting = _isDeleting.value) + .onSuccess { + _uiEvent.emit(UiEvent.SUCCESS) + comment.value = "" + }.onFailure { throwable -> + _uiEvent.emit(UiEvent.ERROR) + Timber.e(throwable) + } + } + } + + fun onClickDone() { + viewModelScope.launch { + if (comment.value.isEmpty()) { + if (_isDeleting.value) { + _isSkip.emit(true) + } + } else { + postFeedback() + } + } + } + + fun initIsDeleting(isDeleting: Boolean) { + _isDeleting.value = isDeleting + } +} diff --git a/app/src/main/java/hous/release/android/presentation/withdraw/withdraw/WithdrawFragment.kt b/app/src/main/java/hous/release/android/presentation/withdraw/withdraw/WithdrawFragment.kt new file mode 100644 index 000000000..1af631ac1 --- /dev/null +++ b/app/src/main/java/hous/release/android/presentation/withdraw/withdraw/WithdrawFragment.kt @@ -0,0 +1,55 @@ +package hous.release.android.presentation.withdraw.withdraw + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import dagger.hilt.android.AndroidEntryPoint +import hous.release.android.R +import hous.release.android.databinding.FragmentWithdrawBinding +import hous.release.android.util.UiEvent +import hous.release.android.util.binding.BindingFragment +import hous.release.android.util.dialog.LoadingDialogFragment +import hous.release.android.util.extension.repeatOnStarted + +@AndroidEntryPoint +class WithdrawFragment : BindingFragment(R.layout.fragment_withdraw) { + private val withdrawViewModel by viewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.viewModel = withdrawViewModel + collectUiEvent() + initBackBtnClickListener() + } + + private fun collectUiEvent() { + repeatOnStarted { + withdrawViewModel.uiEvent.collect { uiEvent -> + val loadingDialogFragment = + childFragmentManager.findFragmentByTag(LoadingDialogFragment.TAG) as? LoadingDialogFragment + + when (uiEvent) { + UiEvent.LOADING -> { + loadingDialogFragment?.show( + childFragmentManager, LoadingDialogFragment.TAG + ) + } + UiEvent.SUCCESS -> { + loadingDialogFragment?.dismiss() + findNavController().navigate(R.id.action_withdrawFragment_to_withdrawDoneFragment) + } + UiEvent.ERROR -> { + loadingDialogFragment?.dismiss() + } + } + } + } + } + + private fun initBackBtnClickListener() { + binding.btnWithdrawBack.setOnClickListener { + findNavController().popBackStack() + } + } +} diff --git a/app/src/main/java/hous/release/android/presentation/withdraw/withdraw/WithdrawViewModel.kt b/app/src/main/java/hous/release/android/presentation/withdraw/withdraw/WithdrawViewModel.kt new file mode 100644 index 000000000..710ae4e73 --- /dev/null +++ b/app/src/main/java/hous/release/android/presentation/withdraw/withdraw/WithdrawViewModel.kt @@ -0,0 +1,44 @@ +package hous.release.android.presentation.withdraw.withdraw + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import hous.release.android.util.UiEvent +import hous.release.domain.entity.SplashState +import hous.release.domain.usecase.ClearLocalPrefUseCase +import hous.release.domain.usecase.DeleteUserUseCase +import hous.release.domain.usecase.SetSplashStateUseCase +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject + +@HiltViewModel +class WithdrawViewModel @Inject constructor( + private val deleteUserUseCase: DeleteUserUseCase, + private val clearLocalPrefUseCase: ClearLocalPrefUseCase, + private val setSplashStateUseCase: SetSplashStateUseCase +) : ViewModel() { + private val _uiEvent = MutableSharedFlow() + val uiEvent: SharedFlow = _uiEvent.asSharedFlow() + + val isCheckedWithdraw = MutableStateFlow(false) + + fun deleteUser() { + viewModelScope.launch { + _uiEvent.emit(UiEvent.LOADING) + deleteUserUseCase() + .onSuccess { + clearLocalPrefUseCase() + setSplashStateUseCase(SplashState.LOGIN) + _uiEvent.emit(UiEvent.SUCCESS) + }.onFailure { throwable -> + _uiEvent.emit(UiEvent.ERROR) + Timber.e(throwable) + } + } + } +} diff --git a/app/src/main/java/hous/release/android/presentation/withdraw/withdrawDone/WithdrawDoneFragment.kt b/app/src/main/java/hous/release/android/presentation/withdraw/withdrawDone/WithdrawDoneFragment.kt new file mode 100644 index 000000000..baa1ea813 --- /dev/null +++ b/app/src/main/java/hous/release/android/presentation/withdraw/withdrawDone/WithdrawDoneFragment.kt @@ -0,0 +1,37 @@ +package hous.release.android.presentation.withdraw.withdrawDone + +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.activity.addCallback +import androidx.core.app.ActivityCompat +import dagger.hilt.android.AndroidEntryPoint +import hous.release.android.R +import hous.release.android.databinding.FragmentWithdrawDoneBinding +import hous.release.android.presentation.login.LoginActivity +import hous.release.android.util.binding.BindingFragment +import hous.release.android.util.extension.setOnSingleClickListener + +@AndroidEntryPoint +class WithdrawDoneFragment : + BindingFragment(R.layout.fragment_withdraw_done) { + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initReturnBtnClickListener() + initBackPressedCallback() + } + + private fun initReturnBtnClickListener() { + binding.tvWithdrawDoneReturn.setOnSingleClickListener { + startActivity(Intent(requireActivity(), LoginActivity::class.java)) + requireActivity().finishAffinity() + } + } + + private fun initBackPressedCallback() { + requireActivity().onBackPressedDispatcher.addCallback { + startActivity(Intent(requireActivity(), LoginActivity::class.java)) + ActivityCompat.finishAffinity(requireActivity()) + } + } +} diff --git a/app/src/main/res/drawable/ic_withdraw_checked.xml b/app/src/main/res/drawable/ic_withdraw_checked.xml index 621796860..997281dc5 100644 --- a/app/src/main/res/drawable/ic_withdraw_checked.xml +++ b/app/src/main/res/drawable/ic_withdraw_checked.xml @@ -1,14 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> - diff --git a/app/src/main/res/drawable/ic_withdraw_feedback_down.xml b/app/src/main/res/drawable/ic_withdraw_feedback_down.xml deleted file mode 100644 index 8052b175b..000000000 --- a/app/src/main/res/drawable/ic_withdraw_feedback_down.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_withdraw_unchecked.xml b/app/src/main/res/drawable/ic_withdraw_unchecked.xml index 8634b0e9d..b33783180 100644 --- a/app/src/main/res/drawable/ic_withdraw_unchecked.xml +++ b/app/src/main/res/drawable/ic_withdraw_unchecked.xml @@ -1,11 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M15.807,8.298A0.924,0.924 0,0 1,16.509 8c0.28,0 0.514,0.1 0.701,0.298A0.923,0.923 0,0 1,17.51 9c0,0.28 -0.1,0.515 -0.299,0.702l-5.894,5.895c-0.21,0.21 -0.48,0.315 -0.807,0.315 -0.316,0 -0.585,-0.105 -0.807,-0.316l-2.895,-2.894A0.924,0.924 0,0 1,6.509 12c0,-0.28 0.1,-0.515 0.298,-0.702A0.96,0.96 0,0 1,7.509 11c0.292,0 0.526,0.1 0.702,0.298l2.894,2.895c0.059,0.059 -0.029,0.1 -0.263,0.123 -0.222,0.023 -0.45,0.023 -0.684,0 -0.222,-0.024 -0.304,-0.065 -0.246,-0.123l5.895,-5.895zM5,17c0,0.632 0.175,1.123 0.526,1.474 0.351,0.35 0.842,0.526 1.474,0.526h10c0.632,0 1.123,-0.175 1.474,-0.526 0.35,-0.351 0.526,-0.842 0.526,-1.474V7c0,-0.632 -0.175,-1.123 -0.526,-1.474C18.123,5.176 17.632,5 17,5H7c-0.632,0 -1.123,0.175 -1.474,0.526C5.176,5.877 5,6.368 5,7v10zM3,7a3.953,3.953 0,0 1,1.982 -3.474A4.027,4.027 0,0 1,7 3h10c0.737,0 1.41,0.175 2.017,0.526 0.62,0.351 1.106,0.837 1.457,1.456 0.35,0.609 0.526,1.281 0.526,2.018v10c0,0.725 -0.175,1.398 -0.526,2.017A3.953,3.953 0,0 1,17 21H7a4.027,4.027 0,0 1,-2.018 -0.526,4.135 4.135,0 0,1 -1.456,-1.457A4.027,4.027 0,0 1,3 17V7z" + android:fillColor="#FFE6E6"/> diff --git a/app/src/main/res/drawable/selector_withdraw_spinner.xml b/app/src/main/res/drawable/selector_withdraw_spinner.xml deleted file mode 100644 index 4fca1f5a8..000000000 --- a/app/src/main/res/drawable/selector_withdraw_spinner.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/activity_profile_edit.xml b/app/src/main/res/layout/activity_profile_edit.xml index 96a125f5b..88ff7d33c 100644 --- a/app/src/main/res/layout/activity_profile_edit.xml +++ b/app/src/main/res/layout/activity_profile_edit.xml @@ -67,7 +67,7 @@ + android:layout_height="wrap_content"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:layout_height="match_parent" + app:defaultNavHost="true" + app:navGraph="@navigation/nav_withdraw" /> diff --git a/app/src/main/res/layout/fragment_feedback.xml b/app/src/main/res/layout/fragment_feedback.xml new file mode 100644 index 000000000..61f82ee67 --- /dev/null +++ b/app/src/main/res/layout/fragment_feedback.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_withdraw.xml b/app/src/main/res/layout/fragment_withdraw.xml new file mode 100644 index 000000000..70e63fccb --- /dev/null +++ b/app/src/main/res/layout/fragment_withdraw.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_withdraw_done.xml b/app/src/main/res/layout/fragment_withdraw_done.xml new file mode 100644 index 000000000..8b92461f8 --- /dev/null +++ b/app/src/main/res/layout/fragment_withdraw_done.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_withdraw.xml b/app/src/main/res/navigation/nav_withdraw.xml new file mode 100644 index 000000000..3b5d00175 --- /dev/null +++ b/app/src/main/res/navigation/nav_withdraw.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 73cd5a787..5a1f064d8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -125,7 +125,7 @@ 추가하기 삭제하기 수정하기 - Rules 편집 + 대표 Rules 편집 가이드 다시보기 제목 설명 @@ -361,17 +361,20 @@ 회원 탈퇴 정말 탈퇴하시겠어요? - 회원탈퇴 이미지 - 지금까지 Hous- 를 이용해주셔서 감사합니다. - 회원을 탈퇴하면 탈퇴 신청 즉시\n회원님의 개인정보는 삭제되며 복구할 수 없습니다.\n\n회원님께서 불편하셨던 점이나 불만사항을\n알려주시면 적극 반영해서 고객님의 불편함을\n해결해 드리도록 노력하는 Hous-가 되겠습니다. - 피드백 남기기 - 사유 선택하기 - 의견 남기기 - %d/200 + 회원을 탈퇴하면 탈퇴 신청 즉시\n회원님의 개인정보는 삭제되며 복구할 수 없습니다. 탈퇴하겠습니다. - 회원 탈퇴하기 - 피드백 스피너 이미지 - 탈퇴되었습니다. + 탈퇴하기 + 그동안 사용해주셔서 감사합니다. + 감사감사요~ + 처음으로 + + + 피드백 보내기 + 보내기 + 건너뛰기 + 더 나은 Hous- 가 될 수 있도록 도와주세요! + 그동안 서비스를 사용하면서 느꼈던 점을 남겨주시면,\n서비스 개선에 적극적으로 반영하겠습니다. + %d/200 네트워크 에러 이미지 diff --git a/data/src/main/java/hous/release/data/datasource/AuthDataSource.kt b/data/src/main/java/hous/release/data/datasource/AuthDataSource.kt index b8c6f8319..eed71f03b 100644 --- a/data/src/main/java/hous/release/data/datasource/AuthDataSource.kt +++ b/data/src/main/java/hous/release/data/datasource/AuthDataSource.kt @@ -1,7 +1,6 @@ package hous.release.data.datasource import android.content.SharedPreferences -import hous.release.data.entity.request.DeleteUserRequest import hous.release.data.entity.request.LoginRequest import hous.release.data.entity.request.SignUpRequest import hous.release.data.entity.response.BaseResponse @@ -9,7 +8,6 @@ import hous.release.data.entity.response.LoginResponse import hous.release.data.entity.response.NoDataResponse import hous.release.data.entity.response.SignUpResponse import hous.release.data.service.AuthService -import hous.release.domain.entity.FeedbackType import javax.inject.Inject class AuthDataSource @Inject constructor( @@ -42,13 +40,8 @@ class AuthDataSource @Inject constructor( ) ) - suspend fun deleteUser(feedbackType: FeedbackType, comment: String): NoDataResponse = - authService.deleteUser( - DeleteUserRequest( - feedbackType = feedbackType.name, - comment = comment - ) - ) + suspend fun deleteUser(): NoDataResponse = + authService.deleteUser() suspend fun postLogout(): NoDataResponse = authService.postLogout() 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 d87c34930..88268537a 100644 --- a/data/src/main/java/hous/release/data/datasource/RuleDataSource.kt +++ b/data/src/main/java/hous/release/data/datasource/RuleDataSource.kt @@ -1,6 +1,5 @@ package hous.release.data.datasource -import hous.release.data.entity.request.DeleteRulesRequest import hous.release.data.entity.request.rule.AddRulesRequest import hous.release.data.entity.request.rule.UpdateRuleRequest import hous.release.data.entity.response.NoDataResponse @@ -51,6 +50,6 @@ class RuleDataSource @Inject constructor(private val ruleService: RuleService) { ) } - suspend fun deleteRuleContent(deleteRules: List): NoDataResponse = - ruleService.deleteRuleContent(DeleteRulesRequest(rulesIdList = deleteRules)) + suspend fun deleteRule(ruleId: Int): NoDataResponse = + ruleService.deleteRule(ruleId) } diff --git a/data/src/main/java/hous/release/data/datasource/SettingsDataSource.kt b/data/src/main/java/hous/release/data/datasource/SettingsDataSource.kt index c1d7cc3f7..d73d8c727 100644 --- a/data/src/main/java/hous/release/data/datasource/SettingsDataSource.kt +++ b/data/src/main/java/hous/release/data/datasource/SettingsDataSource.kt @@ -1,5 +1,6 @@ package hous.release.data.datasource +import hous.release.data.entity.request.FeedbackRequest import hous.release.data.entity.request.NotificationSettingsRequest import hous.release.data.entity.response.BaseResponse import hous.release.data.entity.response.NoDataResponse @@ -21,21 +22,29 @@ class SettingsDataSource @Inject constructor( startTodoStatus: String?, remindTodoStatus: String?, badgeStatus: String? - ): NoDataResponse = - settingsService.patchNotificationSettings( - NotificationSettingsRequest( - isPushNotification = notificationStatus, - rulesPushStatus = newRulesStatus, - newTodoPushStatus = newTodoStatus, - todayTodoPushStatus = startTodoStatus, - remindTodoPushStatus = remindTodoStatus, - badgePushStatus = badgeStatus - ) + ): NoDataResponse = settingsService.patchNotificationSettings( + NotificationSettingsRequest( + isPushNotification = notificationStatus, + rulesPushStatus = newRulesStatus, + newTodoPushStatus = newTodoStatus, + todayTodoPushStatus = startTodoStatus, + remindTodoPushStatus = remindTodoStatus, + badgePushStatus = badgeStatus ) + ) suspend fun getSettingsMyToDo(): BaseResponse = settingsService.getSettingsMyToDo() - suspend fun deleteRoom(): NoDataResponse = - settingsService.deleteRoom() + suspend fun deleteRoom(): NoDataResponse = settingsService.deleteRoom() + + suspend fun postFeedback( + comment: String, + isDeleting: Boolean + ): NoDataResponse = settingsService.postFeedback( + FeedbackRequest( + comment = comment, + isDeleting = isDeleting + ) + ) } diff --git a/data/src/main/java/hous/release/data/entity/request/DeleteUserRequest.kt b/data/src/main/java/hous/release/data/entity/request/DeleteUserRequest.kt deleted file mode 100644 index 69e488931..000000000 --- a/data/src/main/java/hous/release/data/entity/request/DeleteUserRequest.kt +++ /dev/null @@ -1,6 +0,0 @@ -package hous.release.data.entity.request - -data class DeleteUserRequest( - val feedbackType: String, - val comment: String -) diff --git a/data/src/main/java/hous/release/data/entity/request/FeedbackRequest.kt b/data/src/main/java/hous/release/data/entity/request/FeedbackRequest.kt new file mode 100644 index 000000000..85bf8bc29 --- /dev/null +++ b/data/src/main/java/hous/release/data/entity/request/FeedbackRequest.kt @@ -0,0 +1,6 @@ +package hous.release.data.entity.request + +data class FeedbackRequest( + val comment: String, + val isDeleting: Boolean +) diff --git a/data/src/main/java/hous/release/data/repository/AuthRepositoryImpl.kt b/data/src/main/java/hous/release/data/repository/AuthRepositoryImpl.kt index d03ae279f..35f9fa0a9 100644 --- a/data/src/main/java/hous/release/data/repository/AuthRepositoryImpl.kt +++ b/data/src/main/java/hous/release/data/repository/AuthRepositoryImpl.kt @@ -5,14 +5,13 @@ import com.google.firebase.messaging.FirebaseMessaging import hous.release.data.datasource.AuthDataSource import hous.release.data.datasource.LocalPrefTokenDataSource import hous.release.data.datasource.LocalSplashDataSource -import hous.release.domain.entity.FeedbackType import hous.release.domain.entity.SplashState import hous.release.domain.entity.Token import hous.release.domain.entity.response.Login import hous.release.domain.entity.response.SignUp import hous.release.domain.repository.AuthRepository -import javax.inject.Inject import timber.log.Timber +import javax.inject.Inject class AuthRepositoryImpl @Inject constructor( private val authDataSource: AuthDataSource, @@ -75,14 +74,9 @@ class AuthRepositoryImpl @Inject constructor( ) } - override suspend fun deleteUser(feedbackType: FeedbackType, comment: String): Result = + override suspend fun deleteUser(): Result = kotlin.runCatching { - authDataSource.deleteUser( - feedbackType = feedbackType, - comment = comment - ) - }.map { response -> - response.success + authDataSource.deleteUser() } override suspend fun postLogout(): Result = diff --git a/data/src/main/java/hous/release/data/repository/PhotoRepositoryImpl.kt b/data/src/main/java/hous/release/data/repository/PhotoRepositoryImpl.kt index ab5e52aad..fd5a63f2e 100644 --- a/data/src/main/java/hous/release/data/repository/PhotoRepositoryImpl.kt +++ b/data/src/main/java/hous/release/data/repository/PhotoRepositoryImpl.kt @@ -46,17 +46,19 @@ class PhotoRepositoryImpl @Inject constructor( ) // path에 해당하는 remote 사진이 캐시되어 있는지 확인한 후, 캐시되어 있다면 반환한다. - override fun fetchRemotePhotosFlow(photos: List): Flow> = flow { + override fun fetchPhotosFlow(photos: List): Flow> = flow { val _photos = MutableList(photos.size) { null } emit(_photos) photos.forEachIndexed { index, photo -> val photoPath = photo.path val fileName = photoPath.toFileName() if (isCached(fileName)) { // 만약 이미 캐시된 사진이라면 + Timber.d("isCached: $fileName") _photos[index] = Photo.from(File(cacheFolder, fileName).absolutePath) emit(_photos) } else { // 캐시된 사진이 아니라면 val remotePhoto = remoteCacher.cacheImage(photoPath) + Timber.d("remotePhoto: ${remotePhoto?.absolutePath}") remotePhoto?.let { _photos[index] = Photo.from(it.absolutePath) emit(_photos) diff --git a/data/src/main/java/hous/release/data/repository/RuleRepositoryImpl.kt b/data/src/main/java/hous/release/data/repository/RuleRepositoryImpl.kt index 830daae06..64d61f8ed 100644 --- a/data/src/main/java/hous/release/data/repository/RuleRepositoryImpl.kt +++ b/data/src/main/java/hous/release/data/repository/RuleRepositoryImpl.kt @@ -6,13 +6,7 @@ import hous.release.data.entity.request.rule.UpdateRuleRequest import hous.release.domain.entity.rule.DetailRule import hous.release.domain.entity.rule.Rule import hous.release.domain.repository.RuleRepository -import hous.release.domain.util.ApiResult import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.flowOn -import retrofit2.HttpException import java.io.File import javax.inject.Inject @@ -59,16 +53,7 @@ class RuleRepositoryImpl @Inject constructor( ) } - override fun deleteRuleContent(deleteRules: List): Flow> = flow { - val response = ruleDataSource.deleteRuleContent(deleteRules) - if (response.success) { - emit(ApiResult.Success(response.message)) - } else { - emit(ApiResult.Error(response.message)) - } - }.catch { e -> - if (e is HttpException) { - emit(ApiResult.Error(e.message)) - } - }.flowOn(ioDispatcher) + override suspend fun deleteRule(ruleId: Int) { + ruleDataSource.deleteRule(ruleId) + } } diff --git a/data/src/main/java/hous/release/data/repository/SettingsRepositoryImpl.kt b/data/src/main/java/hous/release/data/repository/SettingsRepositoryImpl.kt index 73dd29474..9a1ba47df 100644 --- a/data/src/main/java/hous/release/data/repository/SettingsRepositoryImpl.kt +++ b/data/src/main/java/hous/release/data/repository/SettingsRepositoryImpl.kt @@ -40,4 +40,14 @@ class SettingsRepositoryImpl @Inject constructor( override suspend fun deleteRoom(): Result = kotlin.runCatching { settingsDataSource.deleteRoom() } .map { response -> response.success } + + override suspend fun postFeedback( + comment: String, + isDeleting: Boolean + ): Result = kotlin.runCatching { + settingsDataSource.postFeedback( + comment = comment, + isDeleting = isDeleting + ) + } } diff --git a/data/src/main/java/hous/release/data/service/AuthService.kt b/data/src/main/java/hous/release/data/service/AuthService.kt index c12d6c0e7..fd8e96f7b 100644 --- a/data/src/main/java/hous/release/data/service/AuthService.kt +++ b/data/src/main/java/hous/release/data/service/AuthService.kt @@ -1,6 +1,5 @@ package hous.release.data.service -import hous.release.data.entity.request.DeleteUserRequest import hous.release.data.entity.request.LoginRequest import hous.release.data.entity.request.SignUpRequest import hous.release.data.entity.response.BaseResponse @@ -22,10 +21,8 @@ interface AuthService { @Body body: SignUpRequest ): BaseResponse - @HTTP(method = "DELETE", path = "/v1/user", hasBody = true) - suspend fun deleteUser( - @Body body: DeleteUserRequest - ): NoDataResponse + @HTTP(method = "DELETE", path = "/v2/user", hasBody = true) + suspend fun deleteUser(): NoDataResponse @POST("/v1/auth/logout") suspend fun postLogout(): NoDataResponse 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 52fde0587..1180c6d9d 100644 --- a/data/src/main/java/hous/release/data/service/RuleService.kt +++ b/data/src/main/java/hous/release/data/service/RuleService.kt @@ -1,13 +1,11 @@ package hous.release.data.service -import hous.release.data.entity.request.DeleteRulesRequest import hous.release.data.entity.response.BaseResponse import hous.release.data.entity.response.NoDataResponse import hous.release.data.entity.response.rule.CanAddRuleResponse import hous.release.data.entity.response.rule.DetailRuleResponse import hous.release.data.entity.response.rule.RulesResponse import okhttp3.MultipartBody -import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.HTTP import retrofit2.http.Multipart @@ -42,7 +40,7 @@ interface RuleService { ) @Multipart - @PUT("/v2/rules/{id}") + @PUT("/v2/rule/{id}") suspend fun updateRule( @Path("id") id: Int, @Query("description") description: String, @@ -50,13 +48,13 @@ interface RuleService { @Part images: List ): NoDataResponse - @PUT("/v2/rules/{id}") + @PUT("/v2/rule/{id}") suspend fun updateRuleNoImage( @Path("id") id: Int, @Query("description") description: String, @Query("name") name: String ): NoDataResponse - @HTTP(method = "DELETE", path = "/v1/rules", hasBody = true) - suspend fun deleteRuleContent(@Body body: DeleteRulesRequest): NoDataResponse + @HTTP(method = "DELETE", path = "/v2/rule/{id}") + suspend fun deleteRule(@Path("id") ruleId: Int): NoDataResponse } diff --git a/data/src/main/java/hous/release/data/service/SettingsService.kt b/data/src/main/java/hous/release/data/service/SettingsService.kt index 88aa5e4c2..2edeb5d62 100644 --- a/data/src/main/java/hous/release/data/service/SettingsService.kt +++ b/data/src/main/java/hous/release/data/service/SettingsService.kt @@ -1,5 +1,6 @@ package hous.release.data.service +import hous.release.data.entity.request.FeedbackRequest import hous.release.data.entity.request.NotificationSettingsRequest import hous.release.data.entity.response.BaseResponse import hous.release.data.entity.response.NoDataResponse @@ -9,6 +10,7 @@ import retrofit2.http.Body import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.PATCH +import retrofit2.http.POST interface SettingsService { @GET("/v1/user/push") @@ -24,4 +26,9 @@ interface SettingsService { @DELETE("/v1/room/leave") suspend fun deleteRoom(): NoDataResponse + + @POST("/v1/user/feedback") + suspend fun postFeedback( + @Body body: FeedbackRequest + ): NoDataResponse } diff --git a/domain/src/main/java/hous/release/domain/entity/FeedbackType.kt b/domain/src/main/java/hous/release/domain/entity/FeedbackType.kt deleted file mode 100644 index 8d264df20..000000000 --- a/domain/src/main/java/hous/release/domain/entity/FeedbackType.kt +++ /dev/null @@ -1,10 +0,0 @@ -package hous.release.domain.entity - -enum class FeedbackType(val type: String) { - NO("사유 선택하기"), - DONE_LIVING_TOGETHER("공동생활이 끝나서"), - INCONVENIENT_TO_USE("이용이 불편하고 장애가 많아서"), - LOW_USAGE("사용 빈도가 낮아서"), - CONTENTS_UNSATISFACTORY("콘텐츠 불만"), - ETC("기타") -} diff --git a/domain/src/main/java/hous/release/domain/repository/AuthRepository.kt b/domain/src/main/java/hous/release/domain/repository/AuthRepository.kt index 8ecceb5ea..e1648b68a 100644 --- a/domain/src/main/java/hous/release/domain/repository/AuthRepository.kt +++ b/domain/src/main/java/hous/release/domain/repository/AuthRepository.kt @@ -1,6 +1,5 @@ package hous.release.domain.repository -import hous.release.domain.entity.FeedbackType import hous.release.domain.entity.SplashState import hous.release.domain.entity.Token import hous.release.domain.entity.response.Login @@ -25,7 +24,7 @@ interface AuthRepository { fun getFCMToken(setFCMToken: (String) -> Unit) - suspend fun deleteUser(feedbackType: FeedbackType, comment: String): Result + suspend fun deleteUser(): Result suspend fun postLogout(): Result diff --git a/domain/src/main/java/hous/release/domain/repository/PhotoRepository.kt b/domain/src/main/java/hous/release/domain/repository/PhotoRepository.kt index 275c1999f..18e704936 100644 --- a/domain/src/main/java/hous/release/domain/repository/PhotoRepository.kt +++ b/domain/src/main/java/hous/release/domain/repository/PhotoRepository.kt @@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.Flow import java.io.File interface PhotoRepository { - fun fetchRemotePhotosFlow(paths: List): Flow> + fun fetchPhotosFlow(paths: List): Flow> suspend fun fetchPhotosBy(uris: List): List suspend fun removePhoto(path: String): Boolean suspend fun removeTemporayPhotos(): Boolean 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 57fb6e71e..1b45e50a0 100644 --- a/domain/src/main/java/hous/release/domain/repository/RuleRepository.kt +++ b/domain/src/main/java/hous/release/domain/repository/RuleRepository.kt @@ -2,8 +2,6 @@ package hous.release.domain.repository import hous.release.domain.entity.rule.DetailRule import hous.release.domain.entity.rule.Rule -import hous.release.domain.util.ApiResult -import kotlinx.coroutines.flow.Flow import java.io.File interface RuleRepository { @@ -24,5 +22,5 @@ interface RuleRepository { imageFiles: List ) - fun deleteRuleContent(deleteRules: List): Flow> + suspend fun deleteRule(ruleId: Int) } diff --git a/domain/src/main/java/hous/release/domain/repository/SettingsRepository.kt b/domain/src/main/java/hous/release/domain/repository/SettingsRepository.kt index c5d47ab10..df08ae9ea 100644 --- a/domain/src/main/java/hous/release/domain/repository/SettingsRepository.kt +++ b/domain/src/main/java/hous/release/domain/repository/SettingsRepository.kt @@ -18,4 +18,6 @@ interface SettingsRepository { suspend fun getSettingsMyToDo(): Result suspend fun deleteRoom(): Result + + suspend fun postFeedback(comment: String, isDeleting: Boolean): Result } diff --git a/domain/src/main/java/hous/release/domain/usecase/ClearLocalPrefUseCase.kt b/domain/src/main/java/hous/release/domain/usecase/ClearLocalPrefUseCase.kt new file mode 100644 index 000000000..396fb44b9 --- /dev/null +++ b/domain/src/main/java/hous/release/domain/usecase/ClearLocalPrefUseCase.kt @@ -0,0 +1,10 @@ +package hous.release.domain.usecase + +import hous.release.domain.repository.AuthRepository +import javax.inject.Inject + +class ClearLocalPrefUseCase @Inject constructor( + private val authRepository: AuthRepository +) { + operator fun invoke() = authRepository.clearLocalPref() +} diff --git a/domain/src/main/java/hous/release/domain/usecase/DeleteUserUseCase.kt b/domain/src/main/java/hous/release/domain/usecase/DeleteUserUseCase.kt new file mode 100644 index 000000000..4d42dd979 --- /dev/null +++ b/domain/src/main/java/hous/release/domain/usecase/DeleteUserUseCase.kt @@ -0,0 +1,10 @@ +package hous.release.domain.usecase + +import hous.release.domain.repository.AuthRepository +import javax.inject.Inject + +class DeleteUserUseCase @Inject constructor( + private val authRepository: AuthRepository +) { + suspend operator fun invoke() = authRepository.deleteUser() +} diff --git a/domain/src/main/java/hous/release/domain/usecase/PostFeedbackUseCase.kt b/domain/src/main/java/hous/release/domain/usecase/PostFeedbackUseCase.kt new file mode 100644 index 000000000..02b0e7b70 --- /dev/null +++ b/domain/src/main/java/hous/release/domain/usecase/PostFeedbackUseCase.kt @@ -0,0 +1,11 @@ +package hous.release.domain.usecase + +import hous.release.domain.repository.SettingsRepository +import javax.inject.Inject + +class PostFeedbackUseCase @Inject constructor( + private val settingsRepository: SettingsRepository +) { + suspend operator fun invoke(comment: String, isDeleting: Boolean) = + settingsRepository.postFeedback(comment = comment, isDeleting = isDeleting) +} diff --git a/domain/src/main/java/hous/release/domain/usecase/rule/DeleteOurRulesUseCase.kt b/domain/src/main/java/hous/release/domain/usecase/rule/DeleteOurRulesUseCase.kt deleted file mode 100644 index 1cd86592b..000000000 --- a/domain/src/main/java/hous/release/domain/usecase/rule/DeleteOurRulesUseCase.kt +++ /dev/null @@ -1,8 +0,0 @@ -package hous.release.domain.usecase.rule - -import hous.release.domain.repository.RuleRepository -import javax.inject.Inject - -class DeleteOurRulesUseCase @Inject constructor(private val ourRulesRepository: RuleRepository) { - operator fun invoke(deleteRules: List) = ourRulesRepository.deleteRuleContent(deleteRules) -} diff --git a/domain/src/main/java/hous/release/domain/usecase/rule/DeleteRuleUseCase.kt b/domain/src/main/java/hous/release/domain/usecase/rule/DeleteRuleUseCase.kt new file mode 100644 index 000000000..af375810a --- /dev/null +++ b/domain/src/main/java/hous/release/domain/usecase/rule/DeleteRuleUseCase.kt @@ -0,0 +1,15 @@ +package hous.release.domain.usecase.rule + +import hous.release.domain.repository.PhotoRepository +import hous.release.domain.repository.RuleRepository +import javax.inject.Inject + +class DeleteRuleUseCase @Inject constructor( + private val photoRepository: PhotoRepository, + private val ruleRepository: RuleRepository +) { + suspend operator fun invoke(ruleId: Int, photoPaths: List) { + ruleRepository.deleteRule(ruleId) + photoPaths.forEach { path -> photoRepository.removePhoto(path) } + } +}