refactor StateFlow exposure in ConvosViewModel

This commit is contained in:
2026-05-30 12:01:06 +03:00
parent d428af4ac4
commit 167f980f29
2 changed files with 79 additions and 78 deletions
@@ -55,33 +55,34 @@ class ConvosViewModel(
private val applicationContext: Context,
private val loadConvosByIdUseCase: LoadConvosByIdUseCase
) : ViewModel() {
private val _screenState = MutableStateFlow(ConvosScreenState.EMPTY)
val screenState = _screenState.asStateFlow()
private val _navigation = MutableStateFlow<ConvoNavigation?>(null)
val navigation = _navigation.asStateFlow()
private val screenState = MutableStateFlow(ConvosScreenState.EMPTY)
val screenStateFlow get() = screenState.asStateFlow()
private val _dialog = MutableStateFlow<ConvoDialog?>(null)
val dialog = _dialog.asStateFlow()
private val navigation = MutableStateFlow<ConvoNavigation?>(null)
val navigationFlow get() = navigation.asStateFlow()
private val _convos = MutableStateFlow<List<VkConvo>>(emptyList())
val convos = _convos.asStateFlow()
private val dialog = MutableStateFlow<ConvoDialog?>(null)
val dialogFlow get() = dialog.asStateFlow()
private val _uiConvos = MutableStateFlow<List<UiConvo>>(emptyList())
val uiConvos = _uiConvos.asStateFlow()
private val convos = MutableStateFlow<List<VkConvo>>(emptyList())
val convosFlow get() = convos.asStateFlow()
private val pinnedConvosCount = convos.map { convos ->
private val uiConvos = MutableStateFlow<List<UiConvo>>(emptyList())
val uiConvosFlow get() = uiConvos.asStateFlow()
private val pinnedConvosCount = convosFlow.map { convos ->
convos.count(VkConvo::isPinned)
}.stateIn(viewModelScope, SharingStarted.Eagerly, 0)
private val _baseError = MutableStateFlow<BaseError?>(null)
val baseError = _baseError.asStateFlow()
private val baseError = MutableStateFlow<BaseError?>(null)
val baseErrorFlow get() = baseError.asStateFlow()
private val _currentOffset = MutableStateFlow(0)
val currentOffset = _currentOffset.asStateFlow()
private val currentOffset = MutableStateFlow(0)
val currentOffsetFlow get() = currentOffset.asStateFlow()
private val _canPaginate = MutableStateFlow(false)
val canPaginate = _canPaginate.asStateFlow()
private val canPaginate = MutableStateFlow(false)
val canPaginateFlow get() = canPaginate.asStateFlow()
private val expandedConvoId = MutableStateFlow(0L)
@@ -90,7 +91,7 @@ class ConvosViewModel(
private val interactionsTimers = hashMapOf<Long, InteractionJob?>()
init {
_screenState.updateValue { copy(isArchive = filter == ConvosFilter.ARCHIVE) }
screenState.updateValue { copy(isArchive = filter == ConvosFilter.ARCHIVE) }
loadConvos()
@@ -110,7 +111,7 @@ class ConvosViewModel(
}
fun onNavigationConsumed() {
_navigation.setValue { null }
navigation.setValue { null }
}
fun onDialogConfirmed(dialog: ConvoDialog, bundle: Bundle) {
@@ -143,7 +144,7 @@ class ConvosViewModel(
}
fun onDialogDismissed(dialog: ConvoDialog) {
_dialog.setValue { null }
this.dialog.setValue { null }
}
fun onDialogItemPicked(dialog: ConvoDialog, bundle: Bundle) {
@@ -157,7 +158,7 @@ class ConvosViewModel(
}
fun onErrorButtonClicked() {
when (baseError.value) {
when (baseErrorFlow.value) {
null -> Unit
is BaseError.ConnectionError,
@@ -170,7 +171,7 @@ class ConvosViewModel(
}
fun onPaginationConditionsMet() {
_currentOffset.update { convos.value.size }
currentOffset.update { convosFlow.value.size }
loadConvos()
}
@@ -181,7 +182,7 @@ class ConvosViewModel(
fun onConvoItemClick(convo: UiConvo) {
collapseConvos()
_navigation.setValue { ConvoNavigation.MessagesHistory(peerId = convo.id) }
navigation.setValue { ConvoNavigation.MessagesHistory(peerId = convo.id) }
}
fun onConvoItemLongClick(convo: UiConvo) {
@@ -198,7 +199,7 @@ class ConvosViewModel(
) {
when (option) {
ConvoOption.Delete -> {
_dialog.setValue { ConvoDialog.ConvoDelete(convo.id) }
dialog.setValue { ConvoDialog.ConvoDelete(convo.id) }
}
ConvoOption.MarkAsRead -> {
@@ -212,37 +213,37 @@ class ConvosViewModel(
}
ConvoOption.Pin -> {
_dialog.setValue { ConvoDialog.ConvoPin(convo.id) }
dialog.setValue { ConvoDialog.ConvoPin(convo.id) }
}
ConvoOption.Unpin -> {
_dialog.setValue { ConvoDialog.ConvoUnpin(convo.id) }
dialog.setValue { ConvoDialog.ConvoUnpin(convo.id) }
}
ConvoOption.Archive -> {
_dialog.setValue { ConvoDialog.ConvoArchive(convo.id) }
dialog.setValue { ConvoDialog.ConvoArchive(convo.id) }
}
ConvoOption.Unarchive -> {
_dialog.setValue { ConvoDialog.ConvoUnarchive(convo.id) }
dialog.setValue { ConvoDialog.ConvoUnarchive(convo.id) }
}
}
}
fun onErrorConsumed() {
_baseError.setValue { null }
baseError.setValue { null }
}
fun setScrollIndex(index: Int) {
_screenState.setValue { old -> old.copy(scrollIndex = index) }
screenState.setValue { old -> old.copy(scrollIndex = index) }
}
fun setScrollOffset(offset: Int) {
_screenState.setValue { old -> old.copy(scrollOffset = offset) }
screenState.setValue { old -> old.copy(scrollOffset = offset) }
}
fun onCreateChatButtonClicked() {
_navigation.setValue { ConvoNavigation.CreateChat }
navigation.setValue { ConvoNavigation.CreateChat }
}
private fun collapseConvos() {
@@ -251,7 +252,7 @@ class ConvosViewModel(
}
private fun loadConvos(
offset: Int = currentOffset.value
offset: Int = currentOffsetFlow.value
) {
convoUseCase.getConvos(
count = LOAD_COUNT,
@@ -261,22 +262,22 @@ class ConvosViewModel(
state.processState(
error = { error ->
val newBaseError = VkUtils.parseError(error)
_baseError.update { newBaseError }
baseError.update { newBaseError }
},
success = { response ->
val convos = response
val fullConvos = if (offset == 0) {
convos
} else {
this.convos.value.plus(convos)
this.convosFlow.value.plus(convos)
}
val itemsCountSufficient = response.size == LOAD_COUNT
val paginationExhausted = !itemsCountSufficient &&
this.convos.value.isNotEmpty()
this.convosFlow.value.isNotEmpty()
_screenState.updateValue {
screenState.updateValue {
copy(isPaginationExhausted = paginationExhausted)
}
@@ -293,13 +294,13 @@ class ConvosViewModel(
convoUseCase.storeConvos(response)
_convos.emit(fullConvos)
this.convos.emit(fullConvos)
syncUiConvos()
_canPaginate.setValue { itemsCountSufficient }
canPaginate.setValue { itemsCountSufficient }
}
)
_screenState.setValue { old ->
screenState.setValue { old ->
old.copy(
isLoading = offset == 0 && state.isLoading(),
isPaginating = offset > 0 && state.isLoading()
@@ -313,17 +314,17 @@ class ConvosViewModel(
state.processState(
error = {},
success = {
val newConvos = convos.value.toMutableList()
val newConvos = convosFlow.value.toMutableList()
val convoIndex =
newConvos.indexOfFirstOrNull { it.id == peerId }
?: return@processState
newConvos.removeAt(convoIndex)
_convos.update { newConvos.sorted() }
convos.update { newConvos.sorted() }
syncUiConvos()
}
)
_screenState.emit(screenState.value.copy(isLoading = state.isLoading()))
screenState.emit(screenStateFlow.value.copy(isLoading = state.isLoading()))
}
}
@@ -346,7 +347,7 @@ class ConvosViewModel(
}
)
_screenState.setValue { old -> old.copy(isLoading = state.isLoading()) }
screenState.setValue { old -> old.copy(isLoading = state.isLoading()) }
}
}
@@ -356,7 +357,7 @@ class ConvosViewModel(
state.processState(
error = {},
success = {
convos.value.find { it.id == peerId }?.let { convo ->
convosFlow.value.find { it.id == peerId }?.let { convo ->
handleChatArchived(
LongPollParsedEvent.ChatArchived(
convo = convo,
@@ -373,7 +374,7 @@ class ConvosViewModel(
private fun handleNewMessage(event: LongPollParsedEvent.NewMessage) {
val message = event.message
val newConvos = convos.value.toMutableList()
val newConvos = convosFlow.value.toMutableList()
val convoIndex =
newConvos.indexOfFirstOrNull { it.id == message.peerId }
@@ -392,7 +393,7 @@ class ConvosViewModel(
.copy(lastMessage = message)
newConvos.add(pinnedConvosCount.value, convo)
_convos.update { newConvos.sorted() }
convos.update { newConvos.sorted() }
syncUiConvos()
}
)
@@ -433,14 +434,14 @@ class ConvosViewModel(
newConvos.add(toPosition, newConvo)
}
_convos.update { newConvos.sorted() }
convos.update { newConvos.sorted() }
syncUiConvos()
}
}
private fun handleEditedMessage(event: LongPollParsedEvent.MessageEdited) {
val message = event.message
val newConvos = convos.value.toMutableList()
val newConvos = convosFlow.value.toMutableList()
val convoIndex = newConvos.indexOfFirstOrNull { it.id == message.peerId }
if (convoIndex == null) { // диалога нет в списке
@@ -452,13 +453,13 @@ class ConvosViewModel(
lastMessageId = message.id,
lastCmId = message.cmId
)
_convos.update { newConvos }
convos.update { newConvos }
syncUiConvos()
}
}
private fun handleReadIncomingMessage(event: LongPollParsedEvent.IncomingMessageRead) {
val newConvos = convos.value.toMutableList()
val newConvos = convosFlow.value.toMutableList()
val convoIndex =
newConvos.indexOfFirstOrNull { it.id == event.peerId }
@@ -472,13 +473,13 @@ class ConvosViewModel(
unreadCount = event.unreadCount
)
_convos.update { newConvos }
convos.update { newConvos }
syncUiConvos()
}
}
private fun handleReadOutgoingMessage(event: LongPollParsedEvent.OutgoingMessageRead) {
val newConvos = convos.value.toMutableList()
val newConvos = convosFlow.value.toMutableList()
val convoIndex =
newConvos.indexOfFirstOrNull { it.id == event.peerId }
@@ -492,7 +493,7 @@ class ConvosViewModel(
unreadCount = event.unreadCount
)
_convos.update { newConvos }
convos.update { newConvos }
syncUiConvos()
}
}
@@ -502,7 +503,7 @@ class ConvosViewModel(
val peerId = event.peerId
val userIds = event.userIds
val newConvos = convos.value.toMutableList()
val newConvos = convosFlow.value.toMutableList()
val convoAndIndex =
newConvos.findWithIndex { it.id == peerId }
@@ -513,7 +514,7 @@ class ConvosViewModel(
interactionIds = userIds
)
_convos.update { newConvos }
convos.update { newConvos }
syncUiConvos()
interactionsTimers[peerId]?.let { interactionJob ->
@@ -545,7 +546,7 @@ class ConvosViewModel(
private fun stopInteraction(peerId: Long, interactionJob: InteractionJob) {
interactionsTimers[peerId] ?: return
val newConvos = convos.value.toMutableList()
val newConvos = convosFlow.value.toMutableList()
val convoAndIndex =
newConvos.findWithIndex { it.id == peerId } ?: return
@@ -555,7 +556,7 @@ class ConvosViewModel(
interactionIds = emptyList()
)
_convos.update { newConvos }
convos.update { newConvos }
syncUiConvos()
interactionJob.timerJob.cancel()
@@ -563,7 +564,7 @@ class ConvosViewModel(
}
private fun handleChatMajorChanged(event: LongPollParsedEvent.ChatMajorChanged) {
val newConvos = convos.value.toMutableList()
val newConvos = convosFlow.value.toMutableList()
val convoIndex =
newConvos.indexOfFirstOrNull { it.id == event.peerId }
@@ -573,13 +574,13 @@ class ConvosViewModel(
newConvos[convoIndex] =
newConvos[convoIndex].copy(majorId = event.majorId)
_convos.setValue { newConvos.sorted() }
convos.setValue { newConvos.sorted() }
syncUiConvos()
}
}
private fun handleChatMinorChanged(event: LongPollParsedEvent.ChatMinorChanged) {
val newConvos = convos.value.toMutableList()
val newConvos = convosFlow.value.toMutableList()
val convoIndex =
newConvos.indexOfFirstOrNull { it.id == event.peerId }
@@ -589,13 +590,13 @@ class ConvosViewModel(
newConvos[convoIndex] =
newConvos[convoIndex].copy(minorId = event.minorId)
_convos.setValue { newConvos.sorted() }
convos.setValue { newConvos.sorted() }
syncUiConvos()
}
}
private fun handleChatClearing(event: LongPollParsedEvent.ChatCleared) {
val newConvos = convos.value.toMutableList()
val newConvos = convosFlow.value.toMutableList()
val convoIndex = newConvos.indexOfFirstOrNull { it.id == event.peerId }
@@ -604,7 +605,7 @@ class ConvosViewModel(
} else {
newConvos.removeAt(convoIndex)
_convos.setValue { newConvos.sorted() }
convos.setValue { newConvos.sorted() }
syncUiConvos()
}
}
@@ -612,7 +613,7 @@ class ConvosViewModel(
private fun handleChatArchived(event: LongPollParsedEvent.ChatArchived) {
val convo = event.convo
val newConvos = convos.value.toMutableList()
val newConvos = convosFlow.value.toMutableList()
when (filter) {
ConvosFilter.BUSINESS_NOTIFY -> Unit
@@ -627,7 +628,7 @@ class ConvosViewModel(
newConvos.removeAt(index)
}
_convos.update { newConvos }
convos.update { newConvos }
syncUiConvos()
}
@@ -641,7 +642,7 @@ class ConvosViewModel(
newConvos.add(pinnedConvosCount.value, convo)
}
_convos.update { newConvos.sorted() }
convos.update { newConvos.sorted() }
syncUiConvos()
}
}
@@ -655,7 +656,7 @@ class ConvosViewModel(
state.processState(
error = {},
success = {
val newConvos = convos.value.toMutableList()
val newConvos = convosFlow.value.toMutableList()
val convoIndex =
newConvos.indexOfFirstOrNull { it.id == peerId }
?: return@listenValue
@@ -663,7 +664,7 @@ class ConvosViewModel(
newConvos[convoIndex] =
newConvos[convoIndex].copy(inRead = startMessageId)
_convos.update { newConvos }
convos.update { newConvos }
syncUiConvos()
}
)
@@ -695,7 +696,7 @@ class ConvosViewModel(
}
private fun syncUiConvos(): List<UiConvo> {
val convos = convos.value
val convos = convosFlow.value
val newUiConvos = convos.map { convo ->
val options = mutableListOf<ConvoOption>()
@@ -705,7 +706,7 @@ class ConvosViewModel(
}
}
val convosSize = this.convos.value.size
val convosSize = this.convosFlow.value.size
val pinnedCount = pinnedConvosCount.value
val canPinOneMoreDialog =
@@ -735,7 +736,7 @@ class ConvosViewModel(
options = options.toImmutableList()
)
}
_uiConvos.setValue { newUiConvos }
uiConvos.setValue { newUiConvos }
return newUiConvos
}
@@ -19,12 +19,12 @@ fun ConvosRoute(
onNavigateToArchive: (() -> Unit)? = null,
onScrolledToTop: () -> Unit,
) {
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
val navigationEvent by viewModel.navigation.collectAsStateWithLifecycle()
val convos by viewModel.uiConvos.collectAsStateWithLifecycle()
val dialog by viewModel.dialog.collectAsStateWithLifecycle()
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
val canPaginate by viewModel.canPaginate.collectAsStateWithLifecycle()
val screenState by viewModel.screenStateFlow.collectAsStateWithLifecycle()
val navigationEvent by viewModel.navigationFlow.collectAsStateWithLifecycle()
val convos by viewModel.uiConvosFlow.collectAsStateWithLifecycle()
val dialog by viewModel.dialogFlow.collectAsStateWithLifecycle()
val baseError by viewModel.baseErrorFlow.collectAsStateWithLifecycle()
val canPaginate by viewModel.canPaginateFlow.collectAsStateWithLifecycle()
LaunchedEffect(navigationEvent) {
val shouldBeConsumed: Boolean = when (val navigation = navigationEvent) {