Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ fun ConversationsListScreen(
// ViewModel state
val entries by viewModel.conversationListEntriesFlow.collectAsStateWithLifecycle()
val rooms by viewModel.getRoomsStateFlow.collectAsStateWithLifecycle()
val visibleRooms by viewModel.visibleRoomsFlow.collectAsStateWithLifecycle()
val isShimmerVisible by viewModel.isShimmerVisible.collectAsStateWithLifecycle()
val isSearchActive by viewModel.isSearchActiveFlow.collectAsStateWithLifecycle()
val searchQuery by viewModel.currentSearchQueryFlow.collectAsStateWithLifecycle()
Expand Down Expand Up @@ -184,8 +185,17 @@ fun ConversationsListScreen(
val hasConversationTagsCapability = state.currentUser?.capabilities?.spreedCapability?.let {
CapabilitiesUtil.hasSpreedFeatureCapability(it, SpreedFeatures.CONVERSATION_TAGS)
} == true
val nonEmptyConversationTags = remember(conversationTags, visibleRooms) {
conversationTags.filter { tag ->
if (tag.type == ConversationTag.TYPE_FAVORITES) {
visibleRooms.any { it.favorite }
} else {
visibleRooms.any { it.tagIds.contains(tag.id) }
}
}
}
val showConversationTagsRow = hasConversationTagsCapability &&
conversationTags.isNotEmpty() &&
nonEmptyConversationTags.isNotEmpty() &&
!isSearchActive &&
!showShareTo &&
!isForward
Expand Down Expand Up @@ -336,7 +346,7 @@ fun ConversationsListScreen(
tagsRowContent = if (showConversationTagsRow) {
{
ConversationTagsRow(
tags = conversationTags,
tags = nonEmptyConversationTags,
selectedTagId = selectedTagFilter,
onTagSelected = { tagId ->
val isFavorites = conversationTags.any {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,26 +255,44 @@ class ConversationsListViewModel @Inject constructor(
selectedTagIsFavoritesFlow.value = tagId != null && isFavorites
}

private val searchDisplayModeFlow = combine(_isSearchActiveFlow, _currentSearchQueryFlow) { active, query ->
when {
!active -> SearchDisplayMode.OFF
query.isEmpty() -> SearchDisplayMode.ALL_CONVERSATIONS
else -> SearchDisplayMode.RESULTS
}
}

/**
* Single source of truth for the [ConversationList] LazyColumn.
* Auto-reacts to rooms, filter, tag filter, search-active and search-result changes.
*/
val conversationListEntriesFlow: StateFlow<List<ConversationListEntry>> = combine(
getRoomsStateFlow,
_filterStateFlow,
combine(_isSearchActiveFlow, _currentSearchQueryFlow) { active, query ->
when {
!active -> SearchDisplayMode.OFF
query.isEmpty() -> SearchDisplayMode.ALL_CONVERSATIONS
else -> SearchDisplayMode.RESULTS
}
},
searchDisplayModeFlow,
combine(_selectedTagFilterFlow, selectedTagIsFavoritesFlow, ::TagFilterSelection),
combine(searchResultEntries, hideRoomToken, ::Pair)
) { rooms, filterState, searchMode, tagFilter, (searchResults, hideToken) ->
buildConversationListEntries(rooms, filterState, searchMode, tagFilter, searchResults, hideToken)
}.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())

/**
* Rooms that would actually be shown in the list under the current filter chips
* (mention/unread/archive) and hidden-room/lobby rules, but *before* narrowing to a
* specific tag. Used to decide which tag filter chips currently have a matching,
* visible conversation - e.g. a tag whose only conversation is archived should not
* show up as a chip unless the "Archived" filter is active.
*/
val visibleRoomsFlow: StateFlow<List<ConversationModel>> = combine(
getRoomsStateFlow,
_filterStateFlow,
searchDisplayModeFlow,
hideRoomToken
) { rooms, filterState, searchMode, hideToken ->
baseFilterRooms(rooms, filterState, searchMode, hideToken)
}.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())

/** Clears the tag filter when the filtered-by tag no longer exists (e.g. it was deleted). */
fun clearTagFilterIfMatches(tagId: String) {
if (_selectedTagFilterFlow.value == tagId) {
Expand Down Expand Up @@ -640,6 +658,34 @@ class ConversationsListViewModel @Inject constructor(
): List<ConversationListEntry> {
if (searchMode == SearchDisplayMode.RESULTS) return searchResults

var filtered = baseFilterRooms(rooms, filterState, searchMode, hideToken)

if (searchMode != SearchDisplayMode.ALL_CONVERSATIONS) {
filtered = when {
tagFilter.isFavorites -> filtered.filter { it.favorite }
tagFilter.tagId != null -> filtered.filter { it.tagIds.contains(tagFilter.tagId) }
else -> filtered
}
}

val sorted = filtered.sortedWith(
compareByDescending<ConversationModel> { it.favorite }
.thenByDescending { it.lastActivity }
)
return sorted.map { ConversationListEntry.ConversationEntry(it) }
}

/**
* Applies the hidden-room/lobby rules and the current filter chips (mention/unread/archive),
* but not the tag filter. Shared by [buildConversationListEntries] and [visibleRoomsFlow] so
* that both the list content and the tag-chip visibility agree on what "visible" means.
*/
private fun baseFilterRooms(
rooms: List<ConversationModel>,
filterState: Map<String, Boolean>,
searchMode: SearchDisplayMode,
hideToken: String?
): List<ConversationModel> {
val hasFilterEnabled = filterState[MENTION] == true ||
filterState[UNREAD] == true ||
filterState[ARCHIVE] == true
Expand All @@ -655,25 +701,12 @@ class ConversationsListViewModel @Inject constructor(

filtered = when {
// While search is open with an empty query, all conversations are listed,
// ignoring active filters, the tag filter and the default hiding of archived/future-event rooms
// ignoring active filters and the default hiding of archived/future-event rooms
searchMode == SearchDisplayMode.ALL_CONVERSATIONS -> filtered
hasFilterEnabled -> filtered.filter { filterConversationModel(it, filterState) }
else -> filtered.filter { !isFutureEvent(it) && !it.hasArchived }
}

if (searchMode != SearchDisplayMode.ALL_CONVERSATIONS) {
filtered = when {
tagFilter.isFavorites -> filtered.filter { it.favorite }
tagFilter.tagId != null -> filtered.filter { it.tagIds.contains(tagFilter.tagId) }
else -> filtered
}
}

val sorted = filtered.sortedWith(
compareByDescending<ConversationModel> { it.favorite }
.thenByDescending { it.lastActivity }
)
return sorted.map { ConversationListEntry.ConversationEntry(it) }
return filtered
}

@Suppress("CyclomaticComplexMethod", "NestedBlockDepth")
Expand Down
Loading