Use local room list sorting from Rust. (#2978)

* Switch to the new local room list sorting API

* Get rid of the RoomSummary enum, replace it by the inner RoomSummaryDetails

* And finally, rename RoomSummaryDetails to RoomSummary

* Fix a bunch of warnings

* Small tidy up post rebase/review.

- Remove unused invalidated property.
- Rename some RoomSummary instances to summary instead of details.
- Fix tests with missing roomListItem's.

* Update snapshots

---------

Co-authored-by: Doug <douglase@element.io>
This commit is contained in:
Stefan Ceriu
2024-07-05 12:10:53 +03:00
committed by GitHub
parent c024702d50
commit d71c8c4e83
26 changed files with 556 additions and 608 deletions
+1 -8
View File
@@ -92,14 +92,7 @@ extension ClientProxyMock {
return nil
}
let roomID = room.id ?? UUID().uuidString
switch room {
case .empty:
return await RoomProxyMock(.init(name: "Empty room"))
case .filled(let details), .invalidated(let details):
return await RoomProxyMock(.init(id: roomID, name: details.name))
}
return await RoomProxyMock(.init(id: room.id, name: room.name))
}
}
}
@@ -14022,13 +14022,13 @@ open class RoomListSDKMock: MatrixRustSDK.RoomList {
open var entriesListenerReceivedListener: RoomListEntriesListener?
open var entriesListenerReceivedInvocations: [RoomListEntriesListener] = []
var entriesListenerUnderlyingReturnValue: RoomListEntriesResult!
open var entriesListenerReturnValue: RoomListEntriesResult! {
var entriesListenerUnderlyingReturnValue: TaskHandle!
open var entriesListenerReturnValue: TaskHandle! {
get {
if Thread.isMainThread {
return entriesListenerUnderlyingReturnValue
} else {
var returnValue: RoomListEntriesResult? = nil
var returnValue: TaskHandle? = nil
DispatchQueue.main.sync {
returnValue = entriesListenerUnderlyingReturnValue
}
@@ -14046,9 +14046,9 @@ open class RoomListSDKMock: MatrixRustSDK.RoomList {
}
}
}
open var entriesListenerClosure: ((RoomListEntriesListener) -> RoomListEntriesResult)?
open var entriesListenerClosure: ((RoomListEntriesListener) -> TaskHandle)?
open override func entries(listener: RoomListEntriesListener) -> RoomListEntriesResult {
open override func entries(listener: RoomListEntriesListener) -> TaskHandle {
entriesListenerCallsCount += 1
entriesListenerReceivedListener = listener
DispatchQueue.main.async {
@@ -14436,6 +14436,147 @@ open class RoomListDynamicEntriesControllerSDKMock: MatrixRustSDK.RoomListDynami
}
}
}
open class RoomListEntriesWithDynamicAdaptersResultSDKMock: MatrixRustSDK.RoomListEntriesWithDynamicAdaptersResult {
init() {
super.init(noPointer: .init())
}
public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) {
fatalError("init(unsafeFromRawPointer:) has not been implemented")
}
fileprivate var pointer: UnsafeMutableRawPointer!
//MARK: - controller
var controllerUnderlyingCallsCount = 0
open var controllerCallsCount: Int {
get {
if Thread.isMainThread {
return controllerUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = controllerUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
controllerUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
controllerUnderlyingCallsCount = newValue
}
}
}
}
open var controllerCalled: Bool {
return controllerCallsCount > 0
}
var controllerUnderlyingReturnValue: RoomListDynamicEntriesController!
open var controllerReturnValue: RoomListDynamicEntriesController! {
get {
if Thread.isMainThread {
return controllerUnderlyingReturnValue
} else {
var returnValue: RoomListDynamicEntriesController? = nil
DispatchQueue.main.sync {
returnValue = controllerUnderlyingReturnValue
}
return returnValue!
}
}
set {
if Thread.isMainThread {
controllerUnderlyingReturnValue = newValue
} else {
DispatchQueue.main.sync {
controllerUnderlyingReturnValue = newValue
}
}
}
}
open var controllerClosure: (() -> RoomListDynamicEntriesController)?
open override func controller() -> RoomListDynamicEntriesController {
controllerCallsCount += 1
if let controllerClosure = controllerClosure {
return controllerClosure()
} else {
return controllerReturnValue
}
}
//MARK: - entriesStream
var entriesStreamUnderlyingCallsCount = 0
open var entriesStreamCallsCount: Int {
get {
if Thread.isMainThread {
return entriesStreamUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = entriesStreamUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
entriesStreamUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
entriesStreamUnderlyingCallsCount = newValue
}
}
}
}
open var entriesStreamCalled: Bool {
return entriesStreamCallsCount > 0
}
var entriesStreamUnderlyingReturnValue: TaskHandle!
open var entriesStreamReturnValue: TaskHandle! {
get {
if Thread.isMainThread {
return entriesStreamUnderlyingReturnValue
} else {
var returnValue: TaskHandle? = nil
DispatchQueue.main.sync {
returnValue = entriesStreamUnderlyingReturnValue
}
return returnValue!
}
}
set {
if Thread.isMainThread {
entriesStreamUnderlyingReturnValue = newValue
} else {
DispatchQueue.main.sync {
entriesStreamUnderlyingReturnValue = newValue
}
}
}
}
open var entriesStreamClosure: (() -> TaskHandle)?
open override func entriesStream() -> TaskHandle {
entriesStreamCallsCount += 1
if let entriesStreamClosure = entriesStreamClosure {
return entriesStreamClosure()
} else {
return entriesStreamReturnValue
}
}
}
open class RoomListItemSDKMock: MatrixRustSDK.RoomListItem {
init() {
super.init(noPointer: .init())
@@ -15309,52 +15450,6 @@ open class RoomListServiceSDKMock: MatrixRustSDK.RoomListService {
}
}
//MARK: - applyInput
open var applyInputInputThrowableError: Error?
var applyInputInputUnderlyingCallsCount = 0
open var applyInputInputCallsCount: Int {
get {
if Thread.isMainThread {
return applyInputInputUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = applyInputInputUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
applyInputInputUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
applyInputInputUnderlyingCallsCount = newValue
}
}
}
}
open var applyInputInputCalled: Bool {
return applyInputInputCallsCount > 0
}
open var applyInputInputReceivedInput: RoomListInput?
open var applyInputInputReceivedInvocations: [RoomListInput] = []
open var applyInputInputClosure: ((RoomListInput) async throws -> Void)?
open override func applyInput(input: RoomListInput) async throws {
if let error = applyInputInputThrowableError {
throw error
}
applyInputInputCallsCount += 1
applyInputInputReceivedInput = input
DispatchQueue.main.async {
self.applyInputInputReceivedInvocations.append(input)
}
try await applyInputInputClosure?(input)
}
//MARK: - room
open var roomRoomIdThrowableError: Error?
@@ -54,7 +54,7 @@ extension RoomSummaryProviderMock {
var rooms = initialRooms
if !query.isEmpty {
rooms = rooms.filter { $0.name?.localizedCaseInsensitiveContains(query) ?? false }
rooms = rooms.filter { $0.name.localizedCaseInsensitiveContains(query) }
}
roomListSubject.send(rooms)
@@ -78,126 +78,132 @@ extension RoomSummaryProviderMock {
extension Array where Element == RoomSummary {
static let mockRooms: [Element] = [
.filled(details: RoomSummaryDetails(id: "1",
isInvite: false,
inviter: nil,
name: "Foundation 🔭🪐🌌",
isDirect: false,
avatarURL: nil,
heroes: [],
lastMessage: AttributedString("I do not wish to take the trouble to understand mysticism"),
lastMessageFormattedTimestamp: "14:56",
unreadMessagesCount: 0,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: .allMessages,
canonicalAlias: nil,
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)),
.filled(details: RoomSummaryDetails(id: "2",
isInvite: false,
inviter: nil,
name: "Foundation and Empire",
isDirect: false,
avatarURL: URL.picturesDirectory,
heroes: [],
lastMessage: AttributedString("How do you see the Emperor then? You think he keeps office hours?"),
lastMessageFormattedTimestamp: "2:56 PM",
unreadMessagesCount: 2,
unreadMentionsCount: 0,
unreadNotificationsCount: 2,
notificationMode: .mute,
canonicalAlias: nil,
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)),
.filled(details: RoomSummaryDetails(id: "3",
isInvite: false,
inviter: nil,
name: "Second Foundation",
isDirect: false,
avatarURL: nil,
heroes: [],
lastMessage: try? AttributedString(markdown: "He certainly seemed no *mental genius* to me"),
lastMessageFormattedTimestamp: "Some time ago",
unreadMessagesCount: 3,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: .mentionsAndKeywordsOnly,
canonicalAlias: nil,
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)),
.filled(details: RoomSummaryDetails(id: "4",
isInvite: false,
inviter: nil,
name: "Foundation's Edge",
isDirect: false,
avatarURL: nil,
heroes: [],
lastMessage: AttributedString("There's an archaic measure of time that's called the month"),
lastMessageFormattedTimestamp: "Just now",
unreadMessagesCount: 2,
unreadMentionsCount: 2,
unreadNotificationsCount: 2,
notificationMode: .allMessages,
canonicalAlias: nil,
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)),
.filled(details: RoomSummaryDetails(id: "5",
isInvite: false,
inviter: nil,
name: "Foundation and Earth",
isDirect: true,
avatarURL: nil,
heroes: [],
lastMessage: AttributedString("Clearly, if Earth is powerful enough to do that, it might also be capable of adjusting minds in order to force belief in its radioactivity"),
lastMessageFormattedTimestamp: "1986",
unreadMessagesCount: 1,
unreadMentionsCount: 1,
unreadNotificationsCount: 1,
notificationMode: .allMessages,
canonicalAlias: nil,
hasOngoingCall: true,
isMarkedUnread: false,
isFavourite: false)),
.filled(details: RoomSummaryDetails(id: "6",
isInvite: false,
inviter: nil,
name: "Prelude to Foundation",
isDirect: true,
avatarURL: nil,
heroes: [],
lastMessage: AttributedString("Are you groping for the word 'paranoia'?"),
lastMessageFormattedTimestamp: "きょうはじゅういちがつじゅういちにちです",
unreadMessagesCount: 6,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: .mute,
canonicalAlias: nil,
hasOngoingCall: true,
isMarkedUnread: false,
isFavourite: false)),
.filled(details: RoomSummaryDetails(id: "0",
isInvite: false,
inviter: nil,
name: "Unknown",
isDirect: false,
avatarURL: nil,
heroes: [],
lastMessage: nil,
lastMessageFormattedTimestamp: nil,
unreadMessagesCount: 0,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: nil,
canonicalAlias: nil,
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)),
.empty
RoomSummary(roomListItem: RoomListItemSDKMock(),
id: "1",
isInvite: false,
inviter: nil,
name: "Foundation 🔭🪐🌌",
isDirect: false,
avatarURL: nil,
heroes: [],
lastMessage: AttributedString("I do not wish to take the trouble to understand mysticism"),
lastMessageFormattedTimestamp: "14:56",
unreadMessagesCount: 0,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: .allMessages,
canonicalAlias: nil,
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false),
RoomSummary(roomListItem: RoomListItemSDKMock(),
id: "2",
isInvite: false,
inviter: nil,
name: "Foundation and Empire",
isDirect: false,
avatarURL: URL.picturesDirectory,
heroes: [],
lastMessage: AttributedString("How do you see the Emperor then? You think he keeps office hours?"),
lastMessageFormattedTimestamp: "2:56 PM",
unreadMessagesCount: 2,
unreadMentionsCount: 0,
unreadNotificationsCount: 2,
notificationMode: .mute,
canonicalAlias: nil,
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false),
RoomSummary(roomListItem: RoomListItemSDKMock(),
id: "3",
isInvite: false,
inviter: nil,
name: "Second Foundation",
isDirect: false,
avatarURL: nil,
heroes: [],
lastMessage: try? AttributedString(markdown: "He certainly seemed no *mental genius* to me"),
lastMessageFormattedTimestamp: "Some time ago",
unreadMessagesCount: 3,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: .mentionsAndKeywordsOnly,
canonicalAlias: nil,
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false),
RoomSummary(roomListItem: RoomListItemSDKMock(),
id: "4",
isInvite: false,
inviter: nil,
name: "Foundation's Edge",
isDirect: false,
avatarURL: nil,
heroes: [],
lastMessage: AttributedString("There's an archaic measure of time that's called the month"),
lastMessageFormattedTimestamp: "Just now",
unreadMessagesCount: 2,
unreadMentionsCount: 2,
unreadNotificationsCount: 2,
notificationMode: .allMessages,
canonicalAlias: nil,
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false),
RoomSummary(roomListItem: RoomListItemSDKMock(),
id: "5",
isInvite: false,
inviter: nil,
name: "Foundation and Earth",
isDirect: true,
avatarURL: nil,
heroes: [],
lastMessage: AttributedString("Clearly, if Earth is powerful enough to do that, it might also be capable of adjusting minds in order to force belief in its radioactivity"),
lastMessageFormattedTimestamp: "1986",
unreadMessagesCount: 1,
unreadMentionsCount: 1,
unreadNotificationsCount: 1,
notificationMode: .allMessages,
canonicalAlias: nil,
hasOngoingCall: true,
isMarkedUnread: false,
isFavourite: false),
RoomSummary(roomListItem: RoomListItemSDKMock(),
id: "6",
isInvite: false,
inviter: nil,
name: "Prelude to Foundation",
isDirect: true,
avatarURL: nil,
heroes: [],
lastMessage: AttributedString("Are you groping for the word 'paranoia'?"),
lastMessageFormattedTimestamp: "きょうはじゅういちがつじゅういちにちです",
unreadMessagesCount: 6,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: .mute,
canonicalAlias: nil,
hasOngoingCall: true,
isMarkedUnread: false,
isFavourite: false),
RoomSummary(roomListItem: RoomListItemSDKMock(),
id: "0",
isInvite: false,
inviter: nil,
name: "Unknown",
isDirect: false,
avatarURL: nil,
heroes: [],
lastMessage: nil,
lastMessageFormattedTimestamp: nil,
unreadMessagesCount: 0,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: nil,
canonicalAlias: nil,
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)
]
static let mockRoomsWithNotificationsState: [Element] = {
@@ -213,56 +219,59 @@ extension Array where Element == RoomSummary {
for hasUnreadNotifications in [false, true] {
// Incrementing id value for each combination
let id = result.count + 1
let room = RoomSummary.filled(details: RoomSummaryDetails(id: "\(id)",
settingsMode: mode,
hasUnreadMessages: hasUnreadMessages,
hasUnreadMentions: hasUnreadMentions,
hasUnreadNotifications: hasUnreadNotifications))
let room = RoomSummary(roomListItem: RoomListItemSDKMock(),
id: "\(id)",
settingsMode: mode,
hasUnreadMessages: hasUnreadMessages,
hasUnreadMentions: hasUnreadMentions,
hasUnreadNotifications: hasUnreadNotifications)
result.append(room)
}
}
}
}
return result
}()
static let mockInvites: [Element] = [
.filled(details: RoomSummaryDetails(id: "someAwesomeRoomId1",
isInvite: false,
inviter: RoomMemberProxyMock.mockCharlie,
name: "First room",
isDirect: false,
avatarURL: URL.picturesDirectory,
heroes: [],
lastMessage: nil,
lastMessageFormattedTimestamp: nil,
unreadMessagesCount: 0,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: nil,
canonicalAlias: "#footest:somewhere.org",
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)),
.filled(details: RoomSummaryDetails(id: "someAwesomeRoomId2",
isInvite: false,
inviter: RoomMemberProxyMock.mockCharlie,
name: "Second room",
isDirect: true,
avatarURL: nil,
heroes: [],
lastMessage: nil,
lastMessageFormattedTimestamp: nil,
unreadMessagesCount: 0,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: nil,
canonicalAlias: nil,
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false))
RoomSummary(roomListItem: RoomListItemSDKMock(),
id: "someAwesomeRoomId1",
isInvite: false,
inviter: RoomMemberProxyMock.mockCharlie,
name: "First room",
isDirect: false,
avatarURL: URL.picturesDirectory,
heroes: [],
lastMessage: nil,
lastMessageFormattedTimestamp: nil,
unreadMessagesCount: 0,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: nil,
canonicalAlias: "#footest:somewhere.org",
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false),
RoomSummary(roomListItem: RoomListItemSDKMock(),
id: "someAwesomeRoomId2",
isInvite: false,
inviter: RoomMemberProxyMock.mockCharlie,
name: "Second room",
isDirect: true,
avatarURL: nil,
heroes: [],
lastMessage: nil,
lastMessageFormattedTimestamp: nil,
unreadMessagesCount: 0,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: nil,
canonicalAlias: nil,
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)
]
}
@@ -74,15 +74,10 @@ class GlobalSearchScreenViewModel: GlobalSearchScreenViewModelType, GlobalSearch
private func updateRooms(with summaries: [RoomSummary]) {
state.rooms = summaries.compactMap { summary in
switch summary {
case .empty:
return nil
case .invalidated(let details), .filled(let details):
return GlobalSearchRoom(id: details.id,
name: details.name,
alias: details.canonicalAlias,
avatar: details.avatar)
}
GlobalSearchRoom(id: summary.id,
name: summary.name,
alias: summary.canonicalAlias,
avatar: summary.avatar)
}
}
@@ -152,8 +152,7 @@ struct HomeScreenRoom: Identifiable, Equatable {
static let placeholderLastMessage = AttributedString("Hidden last message")
/// The list item identifier can be a real room identifier, a custom one for invalidated entries
/// or a completely unique one for empty items and skeletons
/// The list item identifier is it's room identifier.
let id: String
/// The real room identifier this item points to
@@ -205,39 +204,39 @@ struct HomeScreenRoom: Identifiable, Equatable {
}
extension HomeScreenRoom {
init(details: RoomSummaryDetails, invalidated: Bool, hideUnreadMessagesBadge: Bool) {
let identifier = invalidated ? "invalidated-" + details.id : details.id
init(summary: RoomSummary, hideUnreadMessagesBadge: Bool) {
let identifier = summary.id
let hasUnreadMessages = hideUnreadMessagesBadge ? false : details.hasUnreadMessages
let hasUnreadMessages = hideUnreadMessagesBadge ? false : summary.hasUnreadMessages
let isDotShown = hasUnreadMessages || details.hasUnreadMentions || details.hasUnreadNotifications || details.isMarkedUnread
let isMentionShown = details.hasUnreadMentions && !details.isMuted
let isMuteShown = details.isMuted
let isCallShown = details.hasOngoingCall
let isHighlighted = details.isMarkedUnread || (!details.isMuted && (details.hasUnreadNotifications || details.hasUnreadMentions))
let isDotShown = hasUnreadMessages || summary.hasUnreadMentions || summary.hasUnreadNotifications || summary.isMarkedUnread
let isMentionShown = summary.hasUnreadMentions && !summary.isMuted
let isMuteShown = summary.isMuted
let isCallShown = summary.hasOngoingCall
let isHighlighted = summary.isMarkedUnread || (!summary.isMuted && (summary.hasUnreadNotifications || summary.hasUnreadMentions))
var inviter: InviterDetails?
if let roomMemberProxy = details.inviter {
if let roomMemberProxy = summary.inviter {
inviter = .init(userID: roomMemberProxy.userID,
displayName: roomMemberProxy.displayName,
avatarURL: roomMemberProxy.avatarURL)
}
self.init(id: identifier,
roomID: details.id,
type: details.isInvite ? .invite : .room,
roomID: summary.id,
type: summary.isInvite ? .invite : .room,
badges: .init(isDotShown: isDotShown,
isMentionShown: isMentionShown,
isMuteShown: isMuteShown,
isCallShown: isCallShown),
name: details.name,
isDirect: details.isDirect,
name: summary.name,
isDirect: summary.isDirect,
isHighlighted: isHighlighted,
isFavourite: details.isFavourite,
timestamp: details.lastMessageFormattedTimestamp,
lastMessage: details.lastMessage,
avatar: details.avatar,
isFavourite: summary.isFavourite,
timestamp: summary.lastMessageFormattedTimestamp,
lastMessage: summary.lastMessage,
avatar: summary.avatar,
inviter: inviter,
canonicalAlias: details.canonicalAlias)
canonicalAlias: summary.canonicalAlias)
}
}
@@ -348,16 +348,8 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
var rooms = [HomeScreenRoom]()
for summary in roomSummaryProvider.roomListPublisher.value {
switch summary {
case .empty:
rooms.append(HomeScreenRoom.placeholder())
case .filled(let details):
let room = HomeScreenRoom(details: details, invalidated: false, hideUnreadMessagesBadge: appSettings.hideUnreadMessagesBadge)
rooms.append(room)
case .invalidated(let details):
let room = HomeScreenRoom(details: details, invalidated: true, hideUnreadMessagesBadge: appSettings.hideUnreadMessagesBadge)
rooms.append(room)
}
let room = HomeScreenRoom(summary: summary, hideUnreadMessagesBadge: appSettings.hideUnreadMessagesBadge)
rooms.append(room)
}
state.rooms = rooms
@@ -207,25 +207,26 @@ private extension HomeScreenRoom {
inviter.displayName = "Jack"
inviter.userID = "@jack:somewhere.com"
let details = RoomSummaryDetails(id: "@someone:somewhere.com",
isInvite: false,
inviter: inviter,
name: "Some Guy",
isDirect: true,
avatarURL: nil,
heroes: [.init(userID: "@someone:somewhere.com")],
lastMessage: nil,
lastMessageFormattedTimestamp: nil,
unreadMessagesCount: 0,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: nil,
canonicalAlias: "#footest:somewhere.org",
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)
let summary = RoomSummary(roomListItem: RoomListItemSDKMock(),
id: "@someone:somewhere.com",
isInvite: false,
inviter: inviter,
name: "Some Guy",
isDirect: true,
avatarURL: nil,
heroes: [.init(userID: "@someone:somewhere.com")],
lastMessage: nil,
lastMessageFormattedTimestamp: nil,
unreadMessagesCount: 0,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: nil,
canonicalAlias: "#footest:somewhere.org",
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)
return .init(details: details, invalidated: false, hideUnreadMessagesBadge: false)
return .init(summary: summary, hideUnreadMessagesBadge: false)
}
static func roomInvite(alias: String? = nil, avatarURL: URL? = nil) -> HomeScreenRoom {
@@ -234,24 +235,25 @@ private extension HomeScreenRoom {
inviter.userID = "@jack:somewhi.nl"
inviter.avatarURL = avatarURL
let details = RoomSummaryDetails(id: "@someone:somewhere.com",
isInvite: false,
inviter: inviter,
name: "Awesome Room",
isDirect: false,
avatarURL: avatarURL,
heroes: [.init(userID: "@someone:somewhere.com")],
lastMessage: nil,
lastMessageFormattedTimestamp: nil,
unreadMessagesCount: 0,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: nil,
canonicalAlias: alias,
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)
let summary = RoomSummary(roomListItem: RoomListItemSDKMock(),
id: "@someone:somewhere.com",
isInvite: false,
inviter: inviter,
name: "Awesome Room",
isDirect: false,
avatarURL: avatarURL,
heroes: [.init(userID: "@someone:somewhere.com")],
lastMessage: nil,
lastMessageFormattedTimestamp: nil,
unreadMessagesCount: 0,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
notificationMode: nil,
canonicalAlias: alias,
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)
return .init(details: details, invalidated: false, hideUnreadMessagesBadge: false)
return .init(summary: summary, hideUnreadMessagesBadge: false)
}
}
@@ -195,14 +195,7 @@ struct HomeScreenRoomCell_Previews: PreviewProvider, TestablePreview {
}()
static func mockRoom(summary: RoomSummary) -> HomeScreenRoom? {
switch summary {
case .empty:
nil
case .invalidated(let details):
HomeScreenRoom(details: details, invalidated: true, hideUnreadMessagesBadge: false)
case .filled(let details):
HomeScreenRoom(details: details, invalidated: false, hideUnreadMessagesBadge: false)
}
HomeScreenRoom(summary: summary, hideUnreadMessagesBadge: false)
}
static var previews: some View {
@@ -84,19 +84,14 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me
MXLog.verbose("Updating rooms")
var rooms = [MessageForwardingRoom]()
for summary in roomSummaryProvider.roomListPublisher.value {
switch summary {
case .empty, .invalidated:
if summary.id == forwardingItem.roomID {
continue
case .filled(let details):
if details.id == forwardingItem.roomID {
continue
}
let room = MessageForwardingRoom(id: details.id, name: details.name, alias: details.canonicalAlias, avatar: details.avatar)
rooms.append(room)
}
let room = MessageForwardingRoom(id: summary.id, name: summary.name, alias: summary.canonicalAlias, avatar: summary.avatar)
rooms.append(room)
}
state.rooms = rooms
@@ -138,21 +138,15 @@ class NotificationSettingsEditScreenViewModel: NotificationSettingsEditScreenVie
var roomsWithUserDefinedMode: [NotificationSettingsEditScreenRoom] = []
for roomSummary in filteredRoomsSummary {
switch roomSummary {
case .empty, .invalidated:
guard let roomProxy = await userSession.clientProxy.roomForIdentifier(roomSummary.id) else { continue }
// `isOneToOneRoom` here is not the same as `isDirect` on the room. From the point of view of the push rule, a one-to-one room is a room with exactly two active members.
let isOneToOneRoom = roomProxy.activeMembersCount == 2
// display only the rooms we're interested in
switch chatType {
case .oneToOneChat where isOneToOneRoom, .groupChat where !isOneToOneRoom:
await roomsWithUserDefinedMode.append(buildRoom(with: roomSummary))
default:
break
case .filled(let details):
guard let roomProxy = await userSession.clientProxy.roomForIdentifier(details.id) else { continue }
// `isOneToOneRoom` here is not the same as `isDirect` on the room. From the point of view of the push rule, a one-to-one room is a room with exactly two active members.
let isOneToOneRoom = roomProxy.activeMembersCount == 2
// display only the rooms we're interested in
switch chatType {
case .oneToOneChat where isOneToOneRoom,
.groupChat where !isOneToOneRoom:
await roomsWithUserDefinedMode.append(buildRoom(with: details))
default:
break
}
}
}
@@ -163,12 +157,12 @@ class NotificationSettingsEditScreenViewModel: NotificationSettingsEditScreenVie
}
}
private func buildRoom(with details: RoomSummaryDetails) async -> NotificationSettingsEditScreenRoom {
let notificationMode = try? await notificationSettingsProxy.getUserDefinedRoomNotificationMode(roomId: details.id)
return NotificationSettingsEditScreenRoom(id: details.id,
roomId: details.id,
name: details.name,
avatar: details.avatar,
private func buildRoom(with summary: RoomSummary) async -> NotificationSettingsEditScreenRoom {
let notificationMode = try? await notificationSettingsProxy.getUserDefinedRoomNotificationMode(roomId: summary.id)
return NotificationSettingsEditScreenRoom(id: summary.id,
roomId: summary.id,
name: summary.name,
avatar: summary.avatar,
notificationMode: notificationMode)
}
@@ -72,7 +72,7 @@ struct NotificationSettingsEditScreen_Previews: PreviewProvider, TestablePreview
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .allMessages
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = [RoomSummary].mockRooms.compactMap(\.id)
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = [RoomSummary].mockRooms.map(\.id)
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@alice:example.com",
roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))))))
var viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat,
@@ -113,7 +113,7 @@ struct NotificationSettingsEditScreen_Previews: PreviewProvider, TestablePreview
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init(canPushEncryptedEvents: true))
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .allMessages
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = [RoomSummary].mockRooms.compactMap(\.id)
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = [RoomSummary].mockRooms.map(\.id)
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@alice:example.com",
roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))))))
var viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat,
@@ -68,17 +68,12 @@ struct NotificationSettingsEditScreenRoomCell_Previews: PreviewProvider, Testabl
notificationSettingsProxy: notificationSettingsProxy)
let rooms: [NotificationSettingsEditScreenRoom] = summaryProvider.roomListPublisher.value.compactMap { summary -> NotificationSettingsEditScreenRoom? in
switch summary {
case .empty, .invalidated:
return nil
case .filled(let details):
return NotificationSettingsEditScreenRoom(id: UUID().uuidString,
roomId: details.id,
name: details.name,
avatar: details.avatar)
}
NotificationSettingsEditScreenRoom(id: UUID().uuidString,
roomId: summary.id,
name: summary.name,
avatar: summary.avatar)
}
return VStack(spacing: 0) {
ForEach(rooms) { room in
NotificationSettingsEditScreenRoomCell(room: room, context: viewModel.context)
@@ -47,7 +47,6 @@ enum ClientProxyError: Error {
enum SlidingSyncConstants {
static let defaultTimelineLimit: UInt = 20
static let maximumVisibleRangeSize = 30
}
/// This struct represents the configuration that we are using to register the application through Pusher to Sygnal
@@ -17,7 +17,9 @@
import Foundation
import MatrixRustSDK
struct RoomSummaryDetails {
struct RoomSummary {
let roomListItem: RoomListItem
let id: String
let isInvite: Bool
@@ -46,9 +48,9 @@ struct RoomSummaryDetails {
var isMuted: Bool { notificationMode == .mute }
}
extension RoomSummaryDetails: CustomStringConvertible {
extension RoomSummary: CustomStringConvertible {
var description: String { """
RoomSummaryDetails: - id: \(id) \
RoomSummary: - id: \(id) \
- isDirect: \(isDirect) \
- unreadMessagesCount: \(unreadMessagesCount) \
- unreadMentionsCount: \(unreadMentionsCount) \
@@ -58,8 +60,9 @@ extension RoomSummaryDetails: CustomStringConvertible {
}
}
extension RoomSummaryDetails {
init(id: String, settingsMode: RoomNotificationModeProxy, hasUnreadMessages: Bool, hasUnreadMentions: Bool, hasUnreadNotifications: Bool) {
extension RoomSummary {
init(roomListItem: RoomListItem, id: String, settingsMode: RoomNotificationModeProxy, hasUnreadMessages: Bool, hasUnreadMentions: Bool, hasUnreadNotifications: Bool) {
self.roomListItem = roomListItem
self.id = id
let string = "\(settingsMode) - messages: \(hasUnreadMessages) - mentions: \(hasUnreadMentions) - notifications: \(hasUnreadNotifications)"
name = string
@@ -35,7 +35,6 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
private var cancellables = Set<AnyCancellable>()
private var listUpdatesSubscriptionResult: RoomListEntriesWithDynamicAdaptersResult?
private var listUpdatesTaskHandle: TaskHandle?
private var stateUpdatesTaskHandle: TaskHandle?
private let roomListSubject = CurrentValueSubject<[RoomSummary], Never>([])
@@ -85,7 +84,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
}
func setRoomList(_ roomList: RoomList) {
guard listUpdatesTaskHandle == nil, stateUpdatesTaskHandle == nil else {
guard stateUpdatesTaskHandle == nil else {
return
}
@@ -101,8 +100,6 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
// Forces the listener above to be called with the current state
setFilter(.all(filters: []))
listUpdatesTaskHandle = listUpdatesSubscriptionResult?.entriesStream
let stateUpdatesSubscriptionResult = try roomList.loadingState(listener: RoomListStateObserver { [weak self] state in
guard let self else { return }
MXLog.info("\(name): Received state update: \(state)")
@@ -119,48 +116,31 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
func updateVisibleRange(_ range: Range<Int>) {
if range.upperBound >= rooms.count {
listUpdatesSubscriptionResult?.controller.addOnePage()
listUpdatesSubscriptionResult?.controller().addOnePage()
} else if range.lowerBound == 0 {
listUpdatesSubscriptionResult?.controller.resetToOnePage()
listUpdatesSubscriptionResult?.controller().resetToOnePage()
}
guard shouldUpdateVisibleRange else {
return
}
Task {
do {
// The scroll view content size based visible range calculations might create large ranges
// This is just a safety check to not overload the backend
var upperBound = range.upperBound
if range.upperBound - range.lowerBound > SlidingSyncConstants.maximumVisibleRangeSize {
upperBound = range.lowerBound + SlidingSyncConstants.maximumVisibleRangeSize
}
MXLog.info("\(name): Setting visible range to \(range.lowerBound)...\(upperBound)")
try await roomListService.applyInput(input: .viewport(ranges: [.init(start: UInt32(range.lowerBound), endInclusive: UInt32(upperBound))]))
} catch {
MXLog.error("Failed updating visible range with error: \(error)")
}
}
}
func setFilter(_ filter: RoomSummaryProviderFilter) {
switch filter {
case .excludeAll:
_ = listUpdatesSubscriptionResult?.controller.setFilter(kind: .none)
_ = listUpdatesSubscriptionResult?.controller().setFilter(kind: .none)
case let .search(query):
let filters: [RoomListEntriesDynamicFilterKind] = if appSettings.fuzzyRoomListSearchEnabled {
[.fuzzyMatchRoomName(pattern: query), .nonLeft]
} else {
[.normalizedMatchRoomName(pattern: query), .nonLeft]
}
_ = listUpdatesSubscriptionResult?.controller.setFilter(kind: .all(filters: filters))
_ = listUpdatesSubscriptionResult?.controller().setFilter(kind: .all(filters: filters))
case let .all(filters):
var filters = filters.map(\.rustFilter)
filters.append(.nonLeft)
_ = listUpdatesSubscriptionResult?.controller.setFilter(kind: .all(filters: filters))
_ = listUpdatesSubscriptionResult?.controller().setFilter(kind: .all(filters: filters))
}
}
@@ -175,13 +155,13 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
MXLog.info("Started processing room list diffs")
MXLog.verbose("\(name): Received \(diffs.count) diffs, current room list \(rooms.compactMap { $0.id ?? "Empty" })")
MXLog.verbose("\(name): Received \(diffs.count) diffs, current room list \(rooms.map(\.id))")
rooms = diffs.reduce(rooms) { currentItems, diff in
processDiff(diff, on: currentItems)
}
MXLog.verbose("\(name): Finished applying \(diffs.count) diffs, new room list \(rooms.compactMap { $0.id ?? "Empty" })")
MXLog.verbose("\(name): Finished applying \(diffs.count) diffs, new room list \(rooms.map(\.id))")
MXLog.info("Finished processing room list diffs")
}
@@ -200,18 +180,17 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
return updatedItems
}
private func fetchRoomDetails(roomID: String) -> (roomInfo: RoomInfo?, latestEvent: EventTimelineItem?) {
private func fetchRoomDetails(from roomListItem: RoomListItem) -> (roomInfo: RoomInfo?, latestEvent: EventTimelineItem?) {
class FetchResult {
var roomInfo: RoomInfo?
var latestEvent: EventTimelineItem?
}
let semaphore = DispatchSemaphore(value: 0)
let result = FetchResult()
Task {
do {
let roomListItem = try roomListService.room(roomId: roomID)
result.latestEvent = await roomListItem.latestEvent()
result.roomInfo = try await roomListItem.roomInfo()
} catch {
@@ -222,12 +201,12 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
semaphore.wait()
return (result.roomInfo, result.latestEvent)
}
private func buildRoomSummaryForIdentifier(_ identifier: String, invalidated: Bool) -> RoomSummary {
let roomDetails = fetchRoomDetails(roomID: identifier)
private func buildRoomSummary(from roomListItem: RoomListItem) -> RoomSummary {
let roomDetails = fetchRoomDetails(from: roomListItem)
guard let roomInfo = roomDetails.roomInfo else {
return .empty
fatalError("Missing room info for \(roomListItem.id())")
}
var attributedLastMessage: AttributedString?
@@ -246,47 +225,24 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
let notificationMode = roomInfo.userDefinedNotificationMode.flatMap { RoomNotificationModeProxy.from(roomNotificationMode: $0) }
let details = RoomSummaryDetails(id: roomInfo.id,
isInvite: roomInfo.membership == .invited,
inviter: inviterProxy,
name: roomInfo.displayName ?? roomInfo.id,
isDirect: roomInfo.isDirect,
avatarURL: roomInfo.avatarUrl.flatMap(URL.init(string:)),
heroes: roomInfo.heroes.map(UserProfileProxy.init),
lastMessage: attributedLastMessage,
lastMessageFormattedTimestamp: lastMessageFormattedTimestamp,
unreadMessagesCount: UInt(roomInfo.numUnreadMessages),
unreadMentionsCount: UInt(roomInfo.numUnreadMentions),
unreadNotificationsCount: UInt(roomInfo.numUnreadNotifications),
notificationMode: notificationMode,
canonicalAlias: roomInfo.canonicalAlias,
hasOngoingCall: roomInfo.hasRoomCall,
isMarkedUnread: roomInfo.isMarkedUnread,
isFavourite: roomInfo.isFavourite)
return invalidated ? .invalidated(details: details) : .filled(details: details)
}
private func buildSummaryForRoomListEntry(_ entry: RoomListEntry) -> RoomSummary {
switch entry {
case .empty:
return .empty
case .filled(let roomId):
return buildRoomSummaryForIdentifier(roomId, invalidated: false)
case .invalidated(let roomId):
guard let cachedRoom = rooms.first(where: { $0.id == roomId }) else {
return buildRoomSummaryForIdentifier(roomId, invalidated: true)
}
switch cachedRoom {
case .empty:
return .empty
case .filled(let details):
return .invalidated(details: details)
case .invalidated:
return cachedRoom
}
}
return RoomSummary(roomListItem: roomListItem,
id: roomInfo.id,
isInvite: roomInfo.membership == .invited,
inviter: inviterProxy,
name: roomInfo.displayName ?? roomInfo.id,
isDirect: roomInfo.isDirect,
avatarURL: roomInfo.avatarUrl.flatMap(URL.init(string:)),
heroes: roomInfo.heroes.map(UserProfileProxy.init),
lastMessage: attributedLastMessage,
lastMessageFormattedTimestamp: lastMessageFormattedTimestamp,
unreadMessagesCount: UInt(roomInfo.numUnreadMessages),
unreadMentionsCount: UInt(roomInfo.numUnreadMentions),
unreadNotificationsCount: UInt(roomInfo.numUnreadNotifications),
notificationMode: notificationMode,
canonicalAlias: roomInfo.canonicalAlias,
hasOngoingCall: roomInfo.hasRoomCall,
isMarkedUnread: roomInfo.isMarkedUnread,
isFavourite: roomInfo.isFavourite)
}
private func buildDiff(from diff: RoomListEntriesUpdate, on rooms: [RoomSummary]) -> CollectionDifference<RoomSummary>? {
@@ -294,10 +250,10 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
switch diff {
case .append(let values):
let debugIdentifiers = values.map(\.debugIdentifier)
let debugIdentifiers = values.map { $0.id() }
MXLog.verbose("\(name): Append \(debugIdentifiers)")
for (index, value) in values.enumerated() {
let summary = buildSummaryForRoomListEntry(value)
let summary = buildRoomSummary(from: value)
changes.append(.insert(offset: rooms.count + index, element: summary, associatedWith: nil))
}
case .clear:
@@ -306,8 +262,8 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
changes.append(.remove(offset: index, element: value, associatedWith: nil))
}
case .insert(let index, let value):
MXLog.verbose("\(name): Insert at \(value.debugIdentifier) at \(index)")
let summary = buildSummaryForRoomListEntry(value)
MXLog.verbose("\(name): Insert at \(value.id()) at \(index)")
let summary = buildRoomSummary(from: value)
changes.append(.insert(offset: Int(index), element: summary, associatedWith: nil))
case .popBack:
MXLog.verbose("\(name): Pop Back")
@@ -321,30 +277,30 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
let summary = rooms[0]
changes.append(.remove(offset: 0, element: summary, associatedWith: nil))
case .pushBack(let value):
MXLog.verbose("\(name): Push Back \(value.debugIdentifier)")
let summary = buildSummaryForRoomListEntry(value)
MXLog.verbose("\(name): Push Back \(value.id())")
let summary = buildRoomSummary(from: value)
changes.append(.insert(offset: rooms.count, element: summary, associatedWith: nil))
case .pushFront(let value):
MXLog.verbose("\(name): Push Front \(value.debugIdentifier)")
let summary = buildSummaryForRoomListEntry(value)
MXLog.verbose("\(name): Push Front \(value.id())")
let summary = buildRoomSummary(from: value)
changes.append(.insert(offset: 0, element: summary, associatedWith: nil))
case .remove(let index):
let summary = rooms[Int(index)]
MXLog.verbose("\(name): Remove \(summary.id ?? "") from \(index)")
MXLog.verbose("\(name): Remove \(summary.id) from \(index)")
changes.append(.remove(offset: Int(index), element: summary, associatedWith: nil))
case .reset(let values):
let debugIdentifiers = values.map(\.debugIdentifier)
let debugIdentifiers = values.map { $0.id() }
MXLog.verbose("\(name): Replace all items with \(debugIdentifiers)")
for (index, summary) in rooms.enumerated() {
changes.append(.remove(offset: index, element: summary, associatedWith: nil))
}
for (index, value) in values.enumerated() {
changes.append(.insert(offset: index, element: buildSummaryForRoomListEntry(value), associatedWith: nil))
changes.append(.insert(offset: index, element: buildRoomSummary(from: value), associatedWith: nil))
}
case .set(let index, let value):
MXLog.verbose("\(name): Update \(value.debugIdentifier) at \(index)")
let summary = buildSummaryForRoomListEntry(value)
MXLog.verbose("\(name): Update \(value.id()) at \(index)")
let summary = buildRoomSummary(from: value)
changes.append(.remove(offset: Int(index), element: summary, associatedWith: nil))
changes.append(.insert(offset: Int(index), element: summary, associatedWith: nil))
case .truncate(let length):
@@ -384,14 +340,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
MXLog.info("\(name): Rebuilding room summaries for \(rooms.count) rooms")
rooms = rooms.map {
switch $0 {
case .empty:
return $0
case .filled(let details):
return self.buildRoomSummaryForIdentifier(details.id, invalidated: false)
case .invalidated(let details):
return self.buildRoomSummaryForIdentifier(details.id, invalidated: true)
}
self.buildRoomSummary(from: $0.roomListItem)
}
MXLog.info("\(name): Finished rebuilding room summaries (\(rooms.count) rooms)")
@@ -409,19 +358,6 @@ extension RoomSummaryProviderState {
}
}
extension MatrixRustSDK.RoomListEntry {
var debugIdentifier: String {
switch self {
case .empty:
return "Empty"
case .invalidated(let roomId):
return "Invalidated(\(roomId))"
case .filled(let roomId):
return "Filled(\(roomId))"
}
}
}
private class RoomListEntriesListenerProxy: RoomListEntriesListener {
private let onUpdateClosure: ([RoomListEntriesUpdate]) -> Void
@@ -41,61 +41,6 @@ enum RoomSummaryProviderState {
}
}
enum RoomSummary: CustomStringConvertible, Equatable {
case empty
case filled(details: RoomSummaryDetails)
case invalidated(details: RoomSummaryDetails)
var id: String? {
switch self {
case .empty:
return nil
case .invalidated(let details), .filled(let details):
return details.id
}
}
var name: String? {
switch self {
case .empty:
return nil
case .invalidated(let details), .filled(let details):
return details.name
}
}
var isDirect: Bool {
switch self {
case .empty:
return false
case .invalidated(let details), .filled(let details):
return details.isDirect
}
}
var description: String {
switch self {
case .empty:
return "\(String(describing: Self.self)): Empty"
case .invalidated(let details):
return "\(String(describing: Self.self)): Invalidated(\(details.id))"
case .filled(let details):
return "\(String(describing: Self.self)): Filled(\(details.id))"
}
}
static func == (lhs: RoomSummary, rhs: RoomSummary) -> Bool {
switch (lhs, rhs) {
case (.empty, .empty):
return true
case (.filled(let lhsDetails), .filled(let rhsDetails)), (.invalidated(let lhsDetails), .invalidated(let rhsDetails)):
return lhsDetails.id == rhsDetails.id
default:
return false
}
}
}
enum RoomSummaryProviderFilter: Equatable {
/// Filters out everything
case excludeAll