diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 14a71cf10..6bca242bc 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -5765,7 +5765,7 @@ repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.1.18; + version = 1.1.19; }; }; 821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index eede5be74..b3a5551d5 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -129,8 +129,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-rust-components-swift", "state" : { - "revision" : "f00c834ec4f80c9eea43282b79b368c93ad6bd82", - "version" : "1.1.18" + "revision" : "8f03dc0cdfc8aa7a4110fb3d6561090befc4d0c3", + "version" : "1.1.19" } }, { diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift index 6f576bf9a..1ff2ea29a 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift @@ -98,6 +98,7 @@ struct HomeScreenViewState: BindableState { struct HomeScreenViewStateBindings { var searchQuery = "" + var isSearchFieldFocused = false var alertInfo: AlertInfo? var leaveRoomAlertItem: LeaveRoomAlertItem? diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index 39b07a690..7cd9361d2 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -88,7 +88,17 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol .map(\.bindings.searchQuery) .removeDuplicates() .sink { [weak self] searchQuery in - self?.roomSummaryProvider?.updateFilterPattern(searchQuery) + guard let self else { return } + updateFilter(isSearchFieldFocused: state.bindings.isSearchFieldFocused, searchQuery: searchQuery) + } + .store(in: &cancellables) + + context.$viewState + .map(\.bindings.isSearchFieldFocused) + .removeDuplicates() + .sink { [weak self] isSearchFieldFocused in + guard let self else { return } + updateFilter(isSearchFieldFocused: isSearchFieldFocused, searchQuery: state.bindings.searchQuery) } .store(in: &cancellables) @@ -142,6 +152,16 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol // MARK: - Private + private func updateFilter(isSearchFieldFocused: Bool, searchQuery: String) { + if !isSearchFieldFocused { + roomSummaryProvider?.setFilter(.all) + } else if searchQuery.isEmpty { + roomSummaryProvider?.setFilter(.none) + } else { + roomSummaryProvider?.setFilter(.normalizedMatchRoomName(searchQuery)) + } + } + private func setupRoomSummaryProviderSubscriptions() { guard let roomSummaryProvider, let inviteSummaryProvider else { MXLog.error("Room summary provider unavailable") diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift index 154e2f359..a6d7532b8 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift @@ -23,7 +23,6 @@ struct HomeScreen: View { @ObservedObject var context: HomeScreenViewModel.Context @State private var scrollViewAdapter = ScrollViewAdapter() - @State private var isSearching = false // Bloom components @State private var bloomView: UIView? @@ -56,7 +55,7 @@ struct HomeScreen: View { topSection LazyVStack(spacing: 0) { - HomeScreenRoomList(context: context, isSearching: $isSearching) + HomeScreenRoomList(context: context) } .searchable(text: $context.searchQuery) .compoundSearchField() @@ -100,7 +99,7 @@ struct HomeScreen: View { } } let isTopController = controller.navigationController?.topViewController != controller - let isHidden = isTopController || isSearching + let isHidden = isTopController || context.isSearchFieldFocused if let bloomView { bloomView.isHidden = isHidden UIView.transition(with: bloomView, duration: 1.75, options: .curveEaseInOut) { @@ -196,7 +195,7 @@ struct HomeScreen: View { sessionVerificationBanner } - if context.viewState.hasPendingInvitations, !isSearching { + if context.viewState.hasPendingInvitations, !context.isSearchFieldFocused { HomeScreenInvitesButton(title: L10n.actionInvitesList, hasBadge: context.viewState.hasUnreadPendingInvitations) { context.send(viewAction: .selectInvites) } diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift index c8ba1abea..df9c6d225 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift @@ -20,39 +20,34 @@ struct HomeScreenRoomList: View { @Environment(\.isSearching) var isSearchFieldFocused @ObservedObject var context: HomeScreenViewModel.Context - @Binding var isSearching: Bool var body: some View { content - .onChange(of: isSearchFieldFocused) { isSearching = $0 } + .onChange(of: isSearchFieldFocused) { context.isSearchFieldFocused = $0 } } @ViewBuilder private var content: some View { - if isSearchFieldFocused, context.searchQuery.count == 0 { - EmptyView() - } else { - ForEach(context.viewState.visibleRooms) { room in - if room.isPlaceholder { - HomeScreenRoomCell(room: room, context: context, isSelected: false) - .redacted(reason: .placeholder) - } else { - let isSelected = context.viewState.selectedRoomID == room.id - HomeScreenRoomCell(room: room, context: context, isSelected: isSelected) - .contextMenu { - Button { - context.send(viewAction: .showRoomDetails(roomIdentifier: room.id)) - } label: { - Label(L10n.commonSettings, systemImage: "gearshape") - } - - Button(role: .destructive) { - context.send(viewAction: .leaveRoom(roomIdentifier: room.id)) - } label: { - Label(L10n.actionLeaveRoom, systemImage: "rectangle.portrait.and.arrow.right") - } + ForEach(context.viewState.visibleRooms) { room in + if room.isPlaceholder { + HomeScreenRoomCell(room: room, context: context, isSelected: false) + .redacted(reason: .placeholder) + } else { + let isSelected = context.viewState.selectedRoomID == room.id + HomeScreenRoomCell(room: room, context: context, isSelected: isSelected) + .contextMenu { + Button { + context.send(viewAction: .showRoomDetails(roomIdentifier: room.id)) + } label: { + Label(L10n.commonSettings, systemImage: "gearshape") } - } + + Button(role: .destructive) { + context.send(viewAction: .leaveRoom(roomIdentifier: room.id)) + } label: { + Label(L10n.actionLeaveRoom, systemImage: "rectangle.portrait.and.arrow.right") + } + } } } } diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index c28bc5f00..5e40c4a91 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -241,9 +241,7 @@ class ClientProxy: ClientProxyProtocol { func roomForIdentifier(_ identifier: String) async -> RoomProxyProtocol? { // Try fetching the room from the cold cache (if available) first - var (roomListItem, room) = await Task.dispatch(on: clientQueue) { - self.roomTupleForIdentifier(identifier) - } + var (roomListItem, room) = await roomTupleForIdentifier(identifier) if let roomListItem, let room { return await RoomProxy(roomListItem: roomListItem, @@ -262,9 +260,7 @@ class ClientProxy: ClientProxyProtocol { _ = await roomSummaryProvider.statePublisher.values.first(where: { $0.isLoaded }) } - (roomListItem, room) = await Task.dispatch(on: clientQueue) { - self.roomTupleForIdentifier(identifier) - } + (roomListItem, room) = await roomTupleForIdentifier(identifier) guard let roomListItem else { MXLog.error("Invalid roomListItem for identifier \(identifier)") @@ -519,10 +515,10 @@ class ClientProxy: ClientProxyProtocol { }) } - private func roomTupleForIdentifier(_ identifier: String) -> (RoomListItem?, Room?) { + private func roomTupleForIdentifier(_ identifier: String) async -> (RoomListItem?, Room?) { do { let roomListItem = try roomListService?.room(roomId: identifier) - let fullRoom = roomListItem?.fullRoom() + let fullRoom = await roomListItem?.fullRoom() return (roomListItem, fullRoom) } catch { diff --git a/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift b/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift index 7970b2b75..80fabe363 100644 --- a/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift +++ b/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift @@ -182,7 +182,7 @@ struct MediaUploadingPreprocessor { case .success(let result): switch await generateThumbnailForVideoAt(result.url) { case .success(let thumbnailResult): - let videoSize = ((try? UInt64(FileManager.default.sizeForItem(at: result.url))) ?? 0) ?? 0 + let videoSize = (try? UInt64(FileManager.default.sizeForItem(at: result.url))) ?? 0 let thumbnailSize = (try? UInt64(FileManager.default.sizeForItem(at: thumbnailResult.url))) ?? 0 let thumbnailInfo = ThumbnailInfo(height: UInt64(thumbnailResult.height), diff --git a/ElementX/Sources/Services/Room/RoomProxy.swift b/ElementX/Sources/Services/Room/RoomProxy.swift index 65a0035c3..193755a7e 100644 --- a/ElementX/Sources/Services/Room/RoomProxy.swift +++ b/ElementX/Sources/Services/Room/RoomProxy.swift @@ -271,7 +271,8 @@ class RoomProxy: RoomProxyProtocol { return await Task.dispatch(on: messageSendingDispatchQueue) { do { if let eventID { - try self.room.sendReply(msg: messageContent, inReplyToEventId: eventID, txnId: transactionId) + let replyItem = try self.room.getEventTimelineItemByEventId(eventId: eventID) + try self.room.sendReply(msg: messageContent, replyItem: replyItem, txnId: transactionId) } else { self.room.send(msg: messageContent, txnId: transactionId) } @@ -662,7 +663,7 @@ class RoomProxy: RoomProxyProtocol { do { let data = try Data(contentsOf: imageURL) - return try .success(self.room.uploadAvatar(mimeType: mimeType, data: [UInt8](data))) + return try .success(self.room.uploadAvatar(mimeType: mimeType, data: [UInt8](data), mediaInfo: nil)) } catch { return .failure(.failedUploadingAvatar) } diff --git a/ElementX/Sources/Services/Room/RoomSummary/MockRoomSummaryProvider.swift b/ElementX/Sources/Services/Room/RoomSummary/MockRoomSummaryProvider.swift index 495d45798..324b9c8e4 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/MockRoomSummaryProvider.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/MockRoomSummaryProvider.swift @@ -46,7 +46,7 @@ class MockRoomSummaryProvider: RoomSummaryProviderProtocol { func updateVisibleRange(_ range: Range) { } - func updateFilterPattern(_ pattern: String?) { } + func setFilter(_ filter: RoomSummaryProviderFilter) { } } extension Array where Element == RoomSummary { diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift index 06490faf2..03af1b8e1 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift @@ -91,7 +91,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { }) // Forces the listener above to be called with the current state - updateFilterPattern(nil) + setFilter(.all) listUpdatesTaskHandle = listUpdatesSubscriptionResult?.entriesStream @@ -134,13 +134,15 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { } } - func updateFilterPattern(_ pattern: String?) { - guard let pattern, !pattern.isEmpty else { + func setFilter(_ filter: RoomSummaryProviderFilter) { + switch filter { + case .none: + _ = listUpdatesSubscriptionResult?.controller.setFilter(kind: .none) + case .all: _ = listUpdatesSubscriptionResult?.controller.setFilter(kind: .all) - return + case .normalizedMatchRoomName(let query): + _ = listUpdatesSubscriptionResult?.controller.setFilter(kind: .normalizedMatchRoomName(pattern: query.lowercased())) } - - _ = listUpdatesSubscriptionResult?.controller.setFilter(kind: .normalizedMatchRoomName(pattern: pattern.lowercased())) } // MARK: - Private @@ -156,23 +158,9 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { MXLog.verbose("\(name): Received \(diffs.count) diffs, current room list \(rooms.compactMap { $0.id ?? "Empty" })") - var updatedItems = rooms - for diff in diffs { - let resetDiffChunkingThreshhold = 50 - if case .reset(let values) = diff, values.count > resetDiffChunkingThreshhold { - // Special case resets in order to prevent large updates from blocking the UI - // Render the first resetDiffChunkingThreshhold as a reset and then append the rest to give the UI time to update - updatedItems = processDiff(.reset(values: Array(values[.. { get } @@ -100,5 +106,5 @@ protocol RoomSummaryProviderProtocol { func updateVisibleRange(_ range: Range) - func updateFilterPattern(_ pattern: String?) + func setFilter(_ filter: RoomSummaryProviderFilter) } diff --git a/project.yml b/project.yml index d0abe0aef..4ab8742d5 100644 --- a/project.yml +++ b/project.yml @@ -45,7 +45,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/matrix-org/matrix-rust-components-swift - exactVersion: 1.1.18 + exactVersion: 1.1.19 # path: ../matrix-rust-sdk DesignKit: path: DesignKit