diff --git a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/screen/GroupScreen.kt b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/screen/GroupScreen.kt index 0a046fb..3e64c92 100644 --- a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/screen/GroupScreen.kt +++ b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/screen/GroupScreen.kt @@ -37,7 +37,6 @@ import com.boostcamp.mapisode.mygroup.sideeffect.GroupSideEffect import com.boostcamp.mapisode.mygroup.sideeffect.rememberFlowWithLifecycle import com.boostcamp.mapisode.mygroup.state.GroupState import com.boostcamp.mapisode.mygroup.viewmodel.GroupViewModel -import timber.log.Timber import com.boostcamp.mapisode.mygroup.R as S @Composable @@ -106,14 +105,14 @@ private fun GroupScreen( MapisodeScaffold( modifier = Modifier - .fillMaxSize() - .pointerInput(Unit) { - detectTapGestures( - onPress = { - focusManager.clearFocus() - }, - ) - }, + .fillMaxSize() + .pointerInput(Unit) { + detectTapGestures( + onPress = { + focusManager.clearFocus() + }, + ) + }, isStatusBarPaddingExist = true, topBar = { TopAppBar( diff --git a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupBaseViewModel.kt b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupBaseViewModel.kt index 952759e..3d43f25 100644 --- a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupBaseViewModel.kt +++ b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupBaseViewModel.kt @@ -15,20 +15,20 @@ import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -abstract class GroupBaseViewModel( - initialState: UI_STATE -): ViewModel() { +abstract class GroupBaseViewModel( + initialState: UI_STATE, +) : ViewModel() { private val _state = MutableStateFlow(initialState) val state = _state.asStateFlow() - private val _intent = MutableSharedFlow() - private val _effect = Channel() val effect = _effect.receiveAsFlow() protected val currentState: UI_STATE get() = _state.value + private val _intent = MutableSharedFlow() + /** * initialize the reducer with the intent argument */ diff --git a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupCreationViewModel.kt b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupCreationViewModel.kt index 67c6a68..1114ea7 100644 --- a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupCreationViewModel.kt +++ b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupCreationViewModel.kt @@ -7,13 +7,17 @@ import com.boostcamp.mapisode.mygroup.R import com.boostcamp.mapisode.mygroup.intent.GroupCreationIntent import com.boostcamp.mapisode.mygroup.sideeffect.GroupCreationSideEffect import com.boostcamp.mapisode.mygroup.state.GroupCreationState -import com.boostcamp.mapisode.ui.base.UiIntent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.persistentListOf +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.delay import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import timber.log.Timber import java.util.Date @@ -30,34 +34,39 @@ class GroupCreationViewModel @Inject constructor( ) { private val userId: ConcurrentHashMap = ConcurrentHashMap() + @OptIn(FlowPreview::class) override suspend fun reducer(intent: SharedFlow) { - intent.collectLatest { uiIntent -> - when (uiIntent) { - GroupCreationIntent.Initialize -> { - initializeCreatingGroup() - } + intent.debounce(100L) + .flatMapLatest { value -> + flowOf(value).onEach { delay(300) } + } + .collectLatest { uiIntent -> + when (uiIntent) { + GroupCreationIntent.Initialize -> { + initializeCreatingGroup() + } - GroupCreationIntent.OnBackClick -> { - sendEffect { GroupCreationSideEffect.NavigateToGroupScreen } - } + GroupCreationIntent.OnBackClick -> { + sendEffect { GroupCreationSideEffect.NavigateToGroupScreen } + } - is GroupCreationIntent.OnGroupCreationClick -> { - checkGroupEdit(uiIntent.title, uiIntent.content, uiIntent.imageUrl) - } + is GroupCreationIntent.OnGroupCreationClick -> { + checkGroupEdit(uiIntent.title, uiIntent.content, uiIntent.imageUrl) + } - is GroupCreationIntent.OnPhotoPickerClick -> { - sendState { copy(isSelectingGroupImage = true) } - } + is GroupCreationIntent.OnPhotoPickerClick -> { + sendState { copy(isSelectingGroupImage = true) } + } - is GroupCreationIntent.OnGroupImageSelect -> { - imageApply(uiIntent.imageUrl) - } + is GroupCreationIntent.OnGroupImageSelect -> { + imageApply(uiIntent.imageUrl) + } - is GroupCreationIntent.OnBackToGroupCreation -> { - sendState { copy(isSelectingGroupImage = false) } + is GroupCreationIntent.OnBackToGroupCreation -> { + sendState { copy(isSelectingGroupImage = false) } + } } } - } } private fun initializeCreatingGroup() { diff --git a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupDetailViewModel.kt b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupDetailViewModel.kt index b078b9a..d069473 100644 --- a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupDetailViewModel.kt +++ b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupDetailViewModel.kt @@ -12,13 +12,17 @@ import com.boostcamp.mapisode.mygroup.model.toGroupUiEpisodeModel import com.boostcamp.mapisode.mygroup.model.toGroupUiModel import com.boostcamp.mapisode.mygroup.sideeffect.GroupDetailSideEffect import com.boostcamp.mapisode.mygroup.state.GroupDetailState -import com.boostcamp.mapisode.ui.base.UiIntent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.delay import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import javax.inject.Inject @@ -30,58 +34,64 @@ class GroupDetailViewModel @Inject constructor( ) : GroupBaseViewModel(GroupDetailState()) { private val groupId = mutableStateOf("") + @OptIn(FlowPreview::class) override suspend fun reducer(intent: SharedFlow) { - intent.collectLatest { uiIntent -> - when (uiIntent) { - is GroupDetailIntent.InitializeGroupDetail -> { - getGroupDetail(uiIntent.groupId) - } + intent + .debounce(100L) + .flatMapLatest { value -> + flowOf(value).onEach { delay(300) } + } + .collectLatest { uiIntent -> + when (uiIntent) { + is GroupDetailIntent.InitializeGroupDetail -> { + getGroupDetail(uiIntent.groupId) + } - is GroupDetailIntent.TryGetGroup -> { - tryGetGroup() - } + is GroupDetailIntent.TryGetGroup -> { + tryGetGroup() + } - is GroupDetailIntent.TryGetUserInfo -> { - setGroupMembersInfo() - } + is GroupDetailIntent.TryGetUserInfo -> { + setGroupMembersInfo() + } - is GroupDetailIntent.OnEditClick -> { - sendEffect { GroupDetailSideEffect.NavigateToGroupEditScreen(groupId.value) } - delay(100) - sendState { copy(isGroupLoading = true) } - } + is GroupDetailIntent.OnEditClick -> { + sendEffect { GroupDetailSideEffect.NavigateToGroupEditScreen(groupId.value) } + delay(100) + sendState { copy(isGroupLoading = true) } + } - is GroupDetailIntent.OnBackClick -> { - sendEffect { GroupDetailSideEffect.NavigateToGroupScreen } - } + is GroupDetailIntent.OnBackClick -> { + sendEffect { GroupDetailSideEffect.NavigateToGroupScreen } + } - is GroupDetailIntent.OnEpisodeClick -> { - sendEffect { GroupDetailSideEffect.NavigateToEpisode(uiIntent.episodeId) } - } + is GroupDetailIntent.OnEpisodeClick -> { + sendEffect { GroupDetailSideEffect.NavigateToEpisode(uiIntent.episodeId) } + } - is GroupDetailIntent.OnIssueCodeClick -> { - issueInvitationCode() - } + is GroupDetailIntent.OnIssueCodeClick -> { + issueInvitationCode() + } - is GroupDetailIntent.OnGroupOutClick -> { - sendEffect { GroupDetailSideEffect.WarnGroupOut } - } + is GroupDetailIntent.OnGroupOutClick -> { + sendEffect { GroupDetailSideEffect.WarnGroupOut } + } - is GroupDetailIntent.OnGroupOutConfirm -> { - sendEffect { GroupDetailSideEffect.RemoveDialog } - delay(100) - leaveGroup() - } + is GroupDetailIntent.OnGroupOutConfirm -> { + sendEffect { GroupDetailSideEffect.RemoveDialog } + delay(100) + leaveGroup() + } - is GroupDetailIntent.OnGroupOutCancel -> { - sendEffect { GroupDetailSideEffect.RemoveDialog } - } + is GroupDetailIntent.OnGroupOutCancel -> { + sendEffect { GroupDetailSideEffect.RemoveDialog } + } - is GroupDetailIntent.TryGetGroupEpisodes -> { - getGroupEpisodes() + is GroupDetailIntent.TryGetGroupEpisodes -> { + getGroupEpisodes() + } } } - } } private fun getGroupDetail(groupId: String) { diff --git a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupEditViewModel.kt b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupEditViewModel.kt index 9bbef6a..4a9e2ac 100644 --- a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupEditViewModel.kt +++ b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupEditViewModel.kt @@ -7,11 +7,14 @@ import com.boostcamp.mapisode.mygroup.intent.GroupEditIntent import com.boostcamp.mapisode.mygroup.model.toGroupCreationModel import com.boostcamp.mapisode.mygroup.sideeffect.GroupEditSideEffect import com.boostcamp.mapisode.mygroup.state.GroupEditState -import com.boostcamp.mapisode.ui.base.UiIntent import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.delay import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import javax.inject.Inject @@ -19,36 +22,41 @@ import javax.inject.Inject class GroupEditViewModel @Inject constructor(private val groupRepository: GroupRepository) : GroupBaseViewModel(GroupEditState()) { + @OptIn(FlowPreview::class) override suspend fun reducer(intent: SharedFlow) { - intent.collectLatest { uiIntent -> - when (uiIntent) { - is GroupEditIntent.Initialize -> { - initializeCreatingGroup(uiIntent.groupId) - } + intent.debounce(100L) + .flatMapLatest { value -> + flowOf(value).onEach { delay(300) } + } + .collect { uiIntent -> + when (uiIntent) { + is GroupEditIntent.Initialize -> { + initializeCreatingGroup(uiIntent.groupId) + } - is GroupEditIntent.OnBackClick -> { - sendEffect { GroupEditSideEffect.NavigateToGroupDetailScreen } - } + is GroupEditIntent.OnBackClick -> { + sendEffect { GroupEditSideEffect.NavigateToGroupDetailScreen } + } - is GroupEditIntent.OnGroupEditClick -> { - checkGroupEdit(uiIntent.title, uiIntent.content, uiIntent.imageUrl) - } + is GroupEditIntent.OnGroupEditClick -> { + checkGroupEdit(uiIntent.title, uiIntent.content, uiIntent.imageUrl) + } - GroupEditIntent.DenyPhotoPermission -> sendEffect { - GroupEditSideEffect.ShowToast(R.string.message_error_permission_denied) - } + GroupEditIntent.DenyPhotoPermission -> sendEffect { + GroupEditSideEffect.ShowToast(R.string.message_error_permission_denied) + } - is GroupEditIntent.OnGroupImageSelect -> { - imageApply(uiIntent.imageUrl) - } + is GroupEditIntent.OnGroupImageSelect -> { + imageApply(uiIntent.imageUrl) + } - GroupEditIntent.OnPhotoPickerClick -> { - sendState { copy(isSelectingGroupImage = true) } - } + GroupEditIntent.OnPhotoPickerClick -> { + sendState { copy(isSelectingGroupImage = true) } + } - GroupEditIntent.OnBackToGroupCreation -> sendState { copy(isSelectingGroupImage = false) } + GroupEditIntent.OnBackToGroupCreation -> sendState { copy(isSelectingGroupImage = false) } + } } - } } private fun initializeCreatingGroup(groupId: String) { diff --git a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupJoinViewModel.kt b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupJoinViewModel.kt index 7cb3207..0a60773 100644 --- a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupJoinViewModel.kt +++ b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupJoinViewModel.kt @@ -8,12 +8,16 @@ import com.boostcamp.mapisode.mygroup.intent.GroupJoinIntent import com.boostcamp.mapisode.mygroup.model.toGroupCreationModel import com.boostcamp.mapisode.mygroup.sideeffect.GroupJoinSideEffect import com.boostcamp.mapisode.mygroup.state.GroupJoinState -import com.boostcamp.mapisode.ui.base.UiIntent import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import javax.inject.Inject @@ -36,13 +40,16 @@ class GroupJoinViewModel @Inject constructor( } } + @OptIn(FlowPreview::class) override suspend fun reducer(intent: SharedFlow) { - viewModelScope.launch { - intent.collectLatest { intent -> - - when (intent) { + intent.debounce(100L) + .flatMapLatest { value -> + flowOf(value).onEach { delay(300) } + } + .collectLatest { uiIntent -> + when (uiIntent) { is GroupJoinIntent.TryGetGroup -> { - tryGetGroupByGroupId(intent.inviteCode) + tryGetGroupByGroupId(uiIntent.inviteCode) } is GroupJoinIntent.OnJoinClick -> { @@ -54,7 +61,6 @@ class GroupJoinViewModel @Inject constructor( } } } - } } private fun tryGetGroupByGroupId(inviteCodes: String) { diff --git a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupViewModel.kt b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupViewModel.kt index c8ba771..0fdd91e 100644 --- a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupViewModel.kt +++ b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/viewmodel/GroupViewModel.kt @@ -7,16 +7,16 @@ import com.boostcamp.mapisode.mygroup.R import com.boostcamp.mapisode.mygroup.intent.GroupIntent import com.boostcamp.mapisode.mygroup.sideeffect.GroupSideEffect import com.boostcamp.mapisode.mygroup.state.GroupState -import com.boostcamp.mapisode.ui.base.UiIntent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toPersistentList -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.delay import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -29,26 +29,30 @@ class GroupViewModel @Inject constructor( @OptIn(FlowPreview::class) override suspend fun reducer(intent: SharedFlow) { - intent.debounce(200).collect { uiIntent -> - when (uiIntent) { - GroupIntent.LoadGroups -> { - Timber.e("here") - loadGroups() - } + intent.debounce(100) + .flatMapLatest { value -> + flowOf(value).onEach { delay(300) } + } + .collect { uiIntent -> + when (uiIntent) { + GroupIntent.LoadGroups -> { + Timber.e("here") + loadGroups() + } - GroupIntent.OnJoinClick -> { - navigateToGroupJoinScreen() - } + GroupIntent.OnJoinClick -> { + navigateToGroupJoinScreen() + } - GroupIntent.OnGroupCreateClick -> { - navigateToGroupCreationScreen() - } + GroupIntent.OnGroupCreateClick -> { + navigateToGroupCreationScreen() + } - is GroupIntent.OnGroupDetailClick -> { - navigateToGroupDetailScreen(uiIntent.groupId) + is GroupIntent.OnGroupDetailClick -> { + navigateToGroupDetailScreen(uiIntent.groupId) + } } } - } } private fun loadGroups() {