diff --git a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/intent/GroupDetailIntent.kt b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/intent/GroupDetailIntent.kt index b566d74..065dfaf 100644 --- a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/intent/GroupDetailIntent.kt +++ b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/intent/GroupDetailIntent.kt @@ -6,7 +6,6 @@ import com.boostcamp.mapisode.ui.base.UiIntent @Immutable sealed class GroupDetailIntent : UiIntent { data class InitializeGroupDetail(val groupId: String) : GroupDetailIntent() - data class TryGetGroup(val groupId: String) : GroupDetailIntent() data object TryGetUserInfo : GroupDetailIntent() data object TryGetGroupEpisodes : GroupDetailIntent() data object OnEditClick : GroupDetailIntent() diff --git a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/screen/GroupDetailScreen.kt b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/screen/GroupDetailScreen.kt index b486f22..007d9f1 100644 --- a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/screen/GroupDetailScreen.kt +++ b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/screen/GroupDetailScreen.kt @@ -119,15 +119,13 @@ fun GroupDetailScreen( ) } + LaunchedEffect(Unit) { + viewModel.sendIntent(GroupDetailIntent.InitializeGroupDetail(detail.groupId)) + } + LaunchedEffect(uiState.value) { with(uiState.value) { - if (isGroupIdCaching) { - viewModel.sendIntent(GroupDetailIntent.InitializeGroupDetail(detail.groupId)) - } - if (isGroupLoading) { - viewModel.sendIntent(GroupDetailIntent.TryGetGroup(detail.groupId)) - } - if (!isGroupIdCaching && !isGroupLoading && membersInfo.isEmpty()) { + if (isGroupLoaded) { viewModel.sendIntent(GroupDetailIntent.TryGetUserInfo) } // 최초 진입 시 보이지 않는 탭, 후순위 로딩 diff --git a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/state/GroupCreationState.kt b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/state/GroupCreationState.kt index 6fa8b09..5191098 100644 --- a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/state/GroupCreationState.kt +++ b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/state/GroupCreationState.kt @@ -7,6 +7,7 @@ import com.boostcamp.mapisode.ui.base.UiState @Immutable data class GroupCreationState( val isInitializing: Boolean = false, + val isNavigatedToGroupScreen: Boolean = false, val isSelectingGroupImage: Boolean = false, val group: GroupCreationModel = GroupCreationModel(), ) : UiState diff --git a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/state/GroupDetailState.kt b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/state/GroupDetailState.kt index 61f361e..65d351d 100644 --- a/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/state/GroupDetailState.kt +++ b/feature/mygroup/src/main/java/com/boostcamp/mapisode/mygroup/state/GroupDetailState.kt @@ -7,13 +7,22 @@ import com.boostcamp.mapisode.mygroup.model.GroupUiModel import com.boostcamp.mapisode.ui.base.UiState import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +import java.util.Date @Immutable data class GroupDetailState( - val isGroupIdCaching: Boolean = true, - val isGroupLoading: Boolean = false, + val isGroupLoaded: Boolean = false, + val isNavigated: Boolean = false, val isGroupOwner: Boolean = false, - val group: GroupUiModel? = null, + val group: GroupUiModel = GroupUiModel( + id = "", + adminUser = "", + description = "", + imageUrl = "", + name = "", + members = persistentListOf(), + createdAt = Date(), + ), val membersInfo: ImmutableList = persistentListOf(), val episodes: ImmutableList = persistentListOf(), ) : UiState 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 d069473..3393aaa 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 @@ -1,6 +1,5 @@ package com.boostcamp.mapisode.mygroup.viewmodel -import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.viewModelScope import com.boostcamp.mapisode.datastore.UserPreferenceDataStore import com.boostcamp.mapisode.episode.EpisodeRepository @@ -14,15 +13,13 @@ import com.boostcamp.mapisode.mygroup.sideeffect.GroupDetailSideEffect import com.boostcamp.mapisode.mygroup.state.GroupDetailState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.channelFlow 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 @@ -32,23 +29,13 @@ class GroupDetailViewModel @Inject constructor( private val episodeRepository: EpisodeRepository, private val userPreferenceDataStore: UserPreferenceDataStore, ) : GroupBaseViewModel(GroupDetailState()) { - private val groupId = mutableStateOf("") - @OptIn(FlowPreview::class) override suspend fun reducer(intent: SharedFlow) { - intent - .debounce(100L) - .flatMapLatest { value -> - flowOf(value).onEach { delay(300) } - } - .collectLatest { uiIntent -> + intent.retainFirstIfNavigating() + .collect { uiIntent -> when (uiIntent) { is GroupDetailIntent.InitializeGroupDetail -> { - getGroupDetail(uiIntent.groupId) - } - - is GroupDetailIntent.TryGetGroup -> { - tryGetGroup() + tryGetGroup(uiIntent.groupId) } is GroupDetailIntent.TryGetUserInfo -> { @@ -56,9 +43,7 @@ class GroupDetailViewModel @Inject constructor( } is GroupDetailIntent.OnEditClick -> { - sendEffect { GroupDetailSideEffect.NavigateToGroupEditScreen(groupId.value) } - delay(100) - sendState { copy(isGroupLoading = true) } + navigateToGroupEditScreen(currentState.group.id) } is GroupDetailIntent.OnBackClick -> { @@ -66,7 +51,7 @@ class GroupDetailViewModel @Inject constructor( } is GroupDetailIntent.OnEpisodeClick -> { - sendEffect { GroupDetailSideEffect.NavigateToEpisode(uiIntent.episodeId) } + navigateToEpisode(uiIntent.episodeId) } is GroupDetailIntent.OnIssueCodeClick -> { @@ -94,30 +79,32 @@ class GroupDetailViewModel @Inject constructor( } } - private fun getGroupDetail(groupId: String) { - this.groupId.value = groupId - sendState { - copy( - isGroupIdCaching = false, - isGroupLoading = true, - ) - } + private fun navigateToGroupEditScreen(groupId: String) { + sendEffect { GroupDetailSideEffect.NavigateToGroupEditScreen(groupId) } } - private fun tryGetGroup() { + private fun navigateToEpisode(episodeId: String) { + sendEffect { GroupDetailSideEffect.NavigateToEpisode(episodeId) } + } + + private fun tryGetGroup(groupId: String) { viewModelScope.launch { try { - val group = groupRepository.getGroupByGroupId(groupId.value) - sendState { - copy( - isGroupLoading = false, - group = group.toGroupUiModel(), - ) - } + val group = groupRepository.getGroupByGroupId(groupId) + if (group.adminUser == userPreferenceDataStore.getUserId().first()) { sendState { copy( isGroupOwner = true, + group = group.toGroupUiModel(), + isGroupLoaded = true, + ) + } + } else { + sendState { + copy( + group = group.toGroupUiModel(), + isGroupLoaded = true, ) } } @@ -130,7 +117,7 @@ class GroupDetailViewModel @Inject constructor( private fun issueInvitationCode() { viewModelScope.launch { try { - val code = groupRepository.issueInvitationCode(groupId.value) + val code = groupRepository.issueInvitationCode(currentState.group.id) sendEffect { GroupDetailSideEffect.IssueInvitationCode(code) } } catch (e: Exception) { sendEffect { GroupDetailSideEffect.ShowToast(R.string.message_issue_code_fail) } @@ -139,7 +126,7 @@ class GroupDetailViewModel @Inject constructor( } private fun setGroupMembersInfo() { - val group = currentState.group ?: throw Exception() + val group = currentState.group val members = group.members viewModelScope.launch { @@ -147,7 +134,7 @@ class GroupDetailViewModel @Inject constructor( members.forEach { member -> val userModel = groupRepository.getUserInfoByUserId(member) val userEpisodeModel = groupRepository.getEpisodesByGroupIdAndUserId( - groupId = groupId.value, + groupId = currentState.group.id, userId = member, ) val latestCreatedAt = userEpisodeModel.maxByOrNull { it.createdAt.time }?.createdAt @@ -177,7 +164,7 @@ class GroupDetailViewModel @Inject constructor( private fun leaveGroup() { viewModelScope.launch { val userId = userPreferenceDataStore.getUserId().first() ?: throw Exception() - val groupId = groupId.value + val groupId = currentState.group.id try { groupRepository.leaveGroup(userId, groupId) sendEffect { GroupDetailSideEffect.ShowToast(R.string.message_group_out_success) } @@ -192,7 +179,7 @@ class GroupDetailViewModel @Inject constructor( private fun getGroupEpisodes() { viewModelScope.launch { try { - val episodes = episodeRepository.getEpisodesByGroup(groupId.value) + val episodes = episodeRepository.getEpisodesByGroup(currentState.group.id) sendState { copy( episodes = episodes.map { @@ -209,3 +196,17 @@ class GroupDetailViewModel @Inject constructor( } } } + +fun Flow.retainFirstIfNavigating() = channelFlow { + var isFirst = true + collectLatest { value -> + if (isFirst) launch(Dispatchers.IO){ + isFirst = false + send(value) + if(value is GroupDetailIntent.OnEditClick || value is GroupDetailIntent.OnEpisodeClick){ + delay(300) + } + isFirst = true + } + } +}