From b567fe46b4a99760e16a7b6223da621e895a6de1 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 5 Aug 2024 15:57:55 +0200 Subject: [PATCH 01/12] reverse timeline + additional handling for polls and stickers string representation --- .../Screens/RoomScreen/RoomScreenViewModel.swift | 2 +- .../Room/RoomSummary/RoomEventStringBuilder.swift | 15 ++++++++++++++- .../RoomMessageEventStringBuilder.swift | 12 ++++++------ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index 2e282300b7..299d0aca70 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -676,7 +676,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol private func buildPinnedEventContent(timelineItems: [TimelineItemProxy]) { var pinnedEventContents = OrderedDictionary() - for item in timelineItems { + for item in timelineItems.reversed() { // Only remote events are pinned if case let .event(event) = item, let eventID = event.id.eventID { diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift index 4b6e3878ef..e47021a02c 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift @@ -42,6 +42,11 @@ struct RoomEventStringBuilder { case .redactedMessage: return prefix(L10n.commonMessageRemoved, with: displayName) case .sticker: + if messageEventStringBuilder.prefix == .type { + var string = AttributedString(L10n.commonSticker) + string.bold() + return string + } return prefix(L10n.commonSticker, with: displayName) case .failedToParseMessageLike, .failedToParseState: return prefix(L10n.commonUnsupportedEvent, with: displayName) @@ -70,6 +75,14 @@ struct RoomEventStringBuilder { memberIsYou: isOutgoing) .map(AttributedString.init) case .poll(let question, _, _, _, _, _, _): + if messageEventStringBuilder.prefix == .type { + let questionPlaceholder = "{question}" + var finalString = AttributedString(L10n.commonPollSummary(questionPlaceholder)) + finalString.bold() + let normalString = AttributedString(question) + finalString.replace(questionPlaceholder, with: normalString) + return finalString + } return prefix(L10n.commonPollSummary(question), with: displayName) case .callInvite: return prefix(L10n.commonCallInvite, with: displayName) @@ -95,7 +108,7 @@ struct RoomEventStringBuilder { RoomEventStringBuilder(stateEventStringBuilder: .init(userID: userID, shouldDisambiguateDisplayNames: false), messageEventStringBuilder: .init(attributedStringBuilder: AttributedStringBuilder(cacheKey: "pinnedEvents", mentionBuilder: PlainMentionBuilder()), - prefix: .mediaType), + prefix: .type), shouldDisambiguateDisplayNames: false, shouldPrefixSenderName: false) } diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomMessageEventStringBuilder.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomMessageEventStringBuilder.swift index 90ae993043..37bba51ca7 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomMessageEventStringBuilder.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomMessageEventStringBuilder.swift @@ -20,7 +20,7 @@ import MatrixRustSDK struct RoomMessageEventStringBuilder { enum Prefix { case senderName - case mediaType + case type case none } @@ -41,19 +41,19 @@ struct RoomMessageEventStringBuilder { case .audio(content: let content): let isVoiceMessage = content.voice != nil var content = AttributedString(isVoiceMessage ? L10n.commonVoiceMessage : L10n.commonAudio) - if prefix == .mediaType { + if prefix == .type { content.bold() } message = content case .image(let content): - message = prefix == .mediaType ? prefix(AttributedString(content.body), with: L10n.commonImage) : AttributedString("\(L10n.commonImage) - \(content.body)") + message = prefix == .type ? prefix(AttributedString(content.body), with: L10n.commonImage) : AttributedString("\(L10n.commonImage) - \(content.body)") case .video(let content): - message = prefix == .mediaType ? prefix(AttributedString(content.body), with: L10n.commonVideo) : AttributedString("\(L10n.commonVideo) - \(content.body)") + message = prefix == .type ? prefix(AttributedString(content.body), with: L10n.commonVideo) : AttributedString("\(L10n.commonVideo) - \(content.body)") case .file(let content): - message = prefix == .mediaType ? prefix(AttributedString(content.body), with: L10n.commonFile) : AttributedString("\(L10n.commonFile) - \(content.body)") + message = prefix == .type ? prefix(AttributedString(content.body), with: L10n.commonFile) : AttributedString("\(L10n.commonFile) - \(content.body)") case .location: var content = AttributedString(L10n.commonSharedLocation) - if prefix == .mediaType { + if prefix == .type { content.bold() } message = content From 604caf9f068989c11fb50554a690baa43f128cc7 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 5 Aug 2024 18:45:44 +0200 Subject: [PATCH 02/12] loading state --- ElementX.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../en.lproj/Localizable.strings | 7 ++ ElementX/Sources/Generated/Strings.swift | 20 ++++ .../Screens/RoomScreen/RoomScreenModels.swift | 93 ++++++++++++++++--- .../RoomScreen/RoomScreenViewModel.swift | 19 +++- .../PinnedItemsBannerView.swift | 56 +++++++---- .../Screens/RoomScreen/View/RoomScreen.swift | 2 +- project.yml | 2 +- 9 files changed, 166 insertions(+), 39 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 561014914e..7aeb7368af 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7561,7 +7561,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.0.31; + version = 1.0.32; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 3d793291f8..fd74b811de 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "8e2b4049fb492dcf5b0c796784b7aa7a3c099943", - "version" : "1.0.31" + "revision" : "9a9d116e5f00b31ad4f727d0875fed13a230806b", + "version" : "1.0.32" } }, { diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index bc19676b60..180d85caf1 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -318,6 +318,7 @@ "screen_room_mentions_at_room_subtitle" = "Notify the whole room"; "screen_room_pinned_banner_indicator" = "%1$@ of %2$@"; "screen_room_pinned_banner_indicator_description" = "%1$@ Pinned messages"; +"screen_room_pinned_banner_loading_description" = "Loading message..."; "screen_room_pinned_banner_view_all_button_title" = "View All"; "screen_account_provider_change" = "Change account provider"; "screen_account_provider_form_hint" = "Homeserver address"; @@ -851,6 +852,12 @@ "state_event_room_name_removed_by_you" = "You removed the room name"; "state_event_room_none" = "%1$@ made no changes"; "state_event_room_none_by_you" = "You made no changes"; +"state_event_room_pinned_events_changed" = "%1$@ changed the pinned messages"; +"state_event_room_pinned_events_changed_by_you" = "You changed the pinned messages"; +"state_event_room_pinned_events_pinned" = "%1$@ pinned a message"; +"state_event_room_pinned_events_pinned_by_you" = "You pinned a message"; +"state_event_room_pinned_events_unpinned" = "%1$@ unpinned a message"; +"state_event_room_pinned_events_unpinned_by_you" = "You unpinned a message"; "state_event_room_reject" = "%1$@ rejected the invitation"; "state_event_room_reject_by_you" = "You rejected the invitation"; "state_event_room_remove" = "%1$@ removed %2$@"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 24b644e5a2..ee77d0e8b2 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -1675,6 +1675,8 @@ internal enum L10n { internal static func screenRoomPinnedBannerIndicatorDescription(_ p1: Any) -> String { return L10n.tr("Localizable", "screen_room_pinned_banner_indicator_description", String(describing: p1)) } + /// Loading message... + internal static var screenRoomPinnedBannerLoadingDescription: String { return L10n.tr("Localizable", "screen_room_pinned_banner_loading_description") } /// View All internal static var screenRoomPinnedBannerViewAllButtonTitle: String { return L10n.tr("Localizable", "screen_room_pinned_banner_view_all_button_title") } /// Send again @@ -2111,6 +2113,24 @@ internal enum L10n { } /// You made no changes internal static var stateEventRoomNoneByYou: String { return L10n.tr("Localizable", "state_event_room_none_by_you") } + /// %1$@ changed the pinned messages + internal static func stateEventRoomPinnedEventsChanged(_ p1: Any) -> String { + return L10n.tr("Localizable", "state_event_room_pinned_events_changed", String(describing: p1)) + } + /// You changed the pinned messages + internal static var stateEventRoomPinnedEventsChangedByYou: String { return L10n.tr("Localizable", "state_event_room_pinned_events_changed_by_you") } + /// %1$@ pinned a message + internal static func stateEventRoomPinnedEventsPinned(_ p1: Any) -> String { + return L10n.tr("Localizable", "state_event_room_pinned_events_pinned", String(describing: p1)) + } + /// You pinned a message + internal static var stateEventRoomPinnedEventsPinnedByYou: String { return L10n.tr("Localizable", "state_event_room_pinned_events_pinned_by_you") } + /// %1$@ unpinned a message + internal static func stateEventRoomPinnedEventsUnpinned(_ p1: Any) -> String { + return L10n.tr("Localizable", "state_event_room_pinned_events_unpinned", String(describing: p1)) + } + /// You unpinned a message + internal static var stateEventRoomPinnedEventsUnpinnedByYou: String { return L10n.tr("Localizable", "state_event_room_pinned_events_unpinned_by_you") } /// %1$@ rejected the invitation internal static func stateEventRoomReject(_ p1: Any) -> String { return L10n.tr("Localizable", "state_event_room_reject", String(describing: p1)) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift index 9a52fde414..0f715c9d65 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift @@ -177,10 +177,10 @@ struct RoomScreenViewState: BindableState { // It's updated from the room info, so it's faster than using the timeline var pinnedEventIDs: Set = [] // This is used to control the banner - var pinnedEventsState = PinnedEventsState() + var pinnedEventsBannerState: PinnedEventsBannerState = .loading(numbersOfEvents: 0) var shouldShowPinnedEventsBanner: Bool { - isPinningEnabled && !pinnedEventsState.pinnedEventContents.isEmpty && lastScrollDirection != .top + isPinningEnabled && !pinnedEventsBannerState.isEmpty && lastScrollDirection != .top } var canJoinCall = false @@ -326,30 +326,101 @@ struct PinnedEventsState: Equatable { } var selectedPinContent: AttributedString { - guard let selectedPinEventID, - var content = pinnedEventContents[selectedPinEventID] else { - return AttributedString() + var content = AttributedString(" ") + if let selectedPinEventID, + var pinnedEventContent = pinnedEventContents[selectedPinEventID] { + content = pinnedEventContent } content.font = .compound.bodyMD return content } + mutating func nextPin() { + guard !pinnedEventContents.isEmpty else { + return + } + let currentIndex = selectedPinIndex + let nextIndex = (currentIndex + 1) % pinnedEventContents.count + selectedPinEventID = pinnedEventContents.keys[nextIndex] + } +} + +enum PinnedEventsBannerState: Equatable { + case loading(numbersOfEvents: Int) + case loaded(state: PinnedEventsState) + + var isEmpty: Bool { + switch self { + case .loaded(let state): + return state.pinnedEventContents.isEmpty + case .loading(let numberOfEvents): + return numberOfEvents == 0 + } + } + + var isLoading: Bool { + switch self { + case .loading: + return true + default: + return false + } + } + + var selectedPinEventID: String? { + switch self { + case .loaded(let state): + return state.selectedPinEventID + default: + return nil + } + } + + var count: Int { + switch self { + case .loaded(let state): + return state.pinnedEventContents.count + case .loading(let numberOfEvents): + return numberOfEvents + } + } + + var selectedPinIndex: Int { + switch self { + case .loaded(let state): + return state.selectedPinIndex + case .loading(let numbersOfEvents): + return numbersOfEvents - 1 + } + } + var bannerIndicatorDescription: AttributedString { let index = selectedPinIndex + 1 let boldPlaceholder = "{bold}" var finalString = AttributedString(L10n.screenRoomPinnedBannerIndicatorDescription(boldPlaceholder)) - var boldString = AttributedString(L10n.screenRoomPinnedBannerIndicator(index, pinnedEventContents.count)) + var boldString = AttributedString(L10n.screenRoomPinnedBannerIndicator(index, count)) boldString.bold() finalString.replace(boldPlaceholder, with: boldString) return finalString } mutating func nextPin() { - guard !pinnedEventContents.isEmpty else { - return + switch self { + case .loaded(var state): + state.nextPin() + self = .loaded(state: state) + default: + break + } + } + + mutating func setPinnedEventContents(_ pinnedEventContents: OrderedDictionary) { + switch self { + case .loading: + self = .loaded(state: .init(pinnedEventContents: pinnedEventContents)) + case .loaded(var state): + state.pinnedEventContents = pinnedEventContents + self = .loaded(state: state) } - let currentIndex = selectedPinIndex - let nextIndex = (currentIndex + 1) % pinnedEventContents.count - selectedPinEventID = pinnedEventContents.keys[nextIndex] } } diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index 299d0aca70..ddad6fe391 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -218,10 +218,10 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol case let .hasScrolled(direction): state.lastScrollDirection = direction case .tappedPinnedEventsBanner: - if let eventID = state.pinnedEventsState.selectedPinEventID { + if let eventID = state.pinnedEventsBannerState.selectedPinEventID { Task { await focusOnEvent(eventID: eventID) } } - state.pinnedEventsState.nextPin() + state.pinnedEventsBannerState.nextPin() case .viewAllPins: // TODO: Implement break @@ -444,12 +444,12 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol return } // If the subscription has sent a value before the Task has started it might be lost, so before entering the loop we always do an update. - await state.pinnedEventIDs = roomProxy.pinnedEventIDs + await updatePinnedEventIDs() for await _ in roomInfoSubscription.receive(on: DispatchQueue.main).values { guard !Task.isCancelled else { return } - await state.pinnedEventIDs = roomProxy.pinnedEventIDs + await updatePinnedEventIDs() } } .store(in: &cancellables) @@ -512,6 +512,15 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } .store(in: &cancellables) } + + private func updatePinnedEventIDs() async { + let pinnedEventIDs = await roomProxy.pinnedEventIDs + // Only update the loading state of the banner + if state.pinnedEventsBannerState.isLoading { + state.pinnedEventsBannerState = .loading(numbersOfEvents: pinnedEventIDs.count) + } + state.pinnedEventIDs = pinnedEventIDs + } private func setupDirectRoomSubscriptionsIfNeeded() { guard roomProxy.isDirect else { @@ -685,7 +694,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } } - state.pinnedEventsState.pinnedEventContents = pinnedEventContents + state.pinnedEventsBannerState.setPinnedEventContents(pinnedEventContents) } private func buildTimelineViews(timelineItems: [RoomTimelineItemProtocol], isSwitchingTimelines: Bool = false) { diff --git a/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift b/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift index f022b46f0e..21b25df3b1 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift @@ -18,11 +18,20 @@ import Compound import SwiftUI struct PinnedItemsBannerView: View { - let pinnedEventsState: PinnedEventsState + let state: PinnedEventsBannerState let onMainButtonTap: () -> Void let onViewAllButtonTap: () -> Void + private var displayedMessage: AttributedString { + switch state { + case .loading: + return AttributedString(L10n.screenRoomPinnedBannerLoadingDescription) + case .loaded(let state): + return state.selectedPinContent + } + } + var body: some View { HStack(spacing: 0) { mainButton @@ -38,7 +47,7 @@ struct PinnedItemsBannerView: View { Button { onMainButtonTap() } label: { HStack(spacing: 0) { HStack(spacing: 10) { - PinnedItemsIndicatorView(pinIndex: pinnedEventsState.selectedPinIndex, pinsCount: pinnedEventsState.pinnedEventContents.count) + PinnedItemsIndicatorView(pinIndex: state.selectedPinIndex, pinsCount: state.count) .accessibilityHidden(true) CompoundIcon(\.pinSolid, size: .small, relativeTo: .compound.bodyMD) .foregroundColor(Color.compound.iconSecondaryAlpha) @@ -48,26 +57,34 @@ struct PinnedItemsBannerView: View { .frame(maxWidth: .infinity, alignment: .leading) } } + .disabled(state.isLoading) .accessibilityElement(children: .contain) } + @ViewBuilder private var viewAllButton: some View { - Button { onViewAllButtonTap() } label: { - Text(L10n.screenRoomPinnedBannerViewAllButtonTitle) - .font(.compound.bodyMDSemibold) - .foregroundStyle(Color.compound.textPrimary) + switch state { + case .loaded: + Button { onViewAllButtonTap() } label: { + Text(L10n.screenRoomPinnedBannerViewAllButtonTitle) + .font(.compound.bodyMDSemibold) + .foregroundStyle(Color.compound.textPrimary) + .padding(.horizontal, 16) + .padding(.vertical, 5) + } + case .loading: + ProgressView() .padding(.horizontal, 16) - .padding(.vertical, 5) } } private var content: some View { VStack(alignment: .leading, spacing: 0) { - Text(pinnedEventsState.bannerIndicatorDescription) + Text(state.bannerIndicatorDescription) .font(.compound.bodySM) .foregroundColor(.compound.textActionAccent) .lineLimit(1) - Text(pinnedEventsState.selectedPinContent) + Text(displayedMessage) .font(.compound.bodyMD) .foregroundColor(.compound.textPrimary) .lineLimit(1) @@ -87,19 +104,22 @@ struct PinnedItemsBannerView_Previews: PreviewProvider, TestablePreview { static var previews: some View { VStack(spacing: 20) { - PinnedItemsBannerView(pinnedEventsState: .init(pinnedEventContents: ["1": "Content", - "2": "2", - "3": "3"], - selectedPinEventID: "1"), + PinnedItemsBannerView(state: .loaded(state: .init(pinnedEventContents: ["1": "Content", + "2": "2", + "3": "3"], + selectedPinEventID: "1")), + onMainButtonTap: { }, + onViewAllButtonTap: { }) + PinnedItemsBannerView(state: .loaded(state: .init(pinnedEventContents: ["1": "Very very very very long content here", + "2": "2"], + selectedPinEventID: "1")), onMainButtonTap: { }, onViewAllButtonTap: { }) - PinnedItemsBannerView(pinnedEventsState: .init(pinnedEventContents: ["1": "Very very very very long content here", - "2": "2"], - selectedPinEventID: "1"), + PinnedItemsBannerView(state: .loaded(state: .init(pinnedEventContents: ["1": attributedContent], + selectedPinEventID: "1")), onMainButtonTap: { }, onViewAllButtonTap: { }) - PinnedItemsBannerView(pinnedEventsState: .init(pinnedEventContents: ["1": attributedContent], - selectedPinEventID: "1"), + PinnedItemsBannerView(state: .loading(numbersOfEvents: 5), onMainButtonTap: { }, onViewAllButtonTap: { }) } diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift index 6d0227d260..1e2069d4b9 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift @@ -110,7 +110,7 @@ struct RoomScreen: View { } private var pinnedItemsBanner: some View { - PinnedItemsBannerView(pinnedEventsState: context.viewState.pinnedEventsState, + PinnedItemsBannerView(state: context.viewState.pinnedEventsBannerState, onMainButtonTap: { context.send(viewAction: .tappedPinnedEventsBanner) }, onViewAllButtonTap: { context.send(viewAction: .viewAllPins) }) .transition(.move(edge: .top)) diff --git a/project.yml b/project.yml index 91f61e8ea4..83b243ed51 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 1.0.31 + exactVersion: 1.0.32 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios From e8748a20441f107c11fd4cbd0fb73e9a0d8a099d Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 5 Aug 2024 19:14:15 +0200 Subject: [PATCH 03/12] retry setting up the timeline when network is available --- .../RoomScreen/RoomScreenCoordinator.swift | 1 + .../RoomScreen/RoomScreenViewModel.swift | 84 +++++++++++++------ .../ReadReceiptsSummaryView.swift | 1 + .../Screens/RoomScreen/View/RoomScreen.swift | 1 + .../TimelineReadReceiptsView.swift | 1 + .../HighlightedTimelineItemModifier.swift | 1 + .../View/Timeline/TimelineView.swift | 2 +- ...est_pinnedItemsBannerView-iPad-en-GB.1.png | 4 +- ...st_pinnedItemsBannerView-iPad-pseudo.1.png | 4 +- ...innedItemsBannerView-iPhone-15-en-GB.1.png | 4 +- ...nnedItemsBannerView-iPhone-15-pseudo.1.png | 4 +- 11 files changed, 71 insertions(+), 36 deletions(-) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift index 1e4058e6e4..c02b6c27e2 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift @@ -67,6 +67,7 @@ final class RoomScreenCoordinator: CoordinatorProtocol { mediaPlayerProvider: parameters.mediaPlayerProvider, voiceMessageMediaManager: parameters.voiceMessageMediaManager, userIndicatorController: ServiceLocator.shared.userIndicatorController, + networkMonitor: ServiceLocator.shared.networkMonitor, appMediator: parameters.appMediator, appSettings: parameters.appSettings, analyticsService: ServiceLocator.shared.analytics) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index ddad6fe391..f612ef6e88 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -33,6 +33,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol private let timelineController: RoomTimelineControllerProtocol private let mediaPlayerProvider: MediaPlayerProviderProtocol private let userIndicatorController: UserIndicatorControllerProtocol + private let networkMonitor: NetworkMonitorProtocol private let appMediator: AppMediatorProtocol private let appSettings: AppSettings private let analyticsService: AnalyticsService @@ -49,6 +50,24 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol private var paginateBackwardsTask: Task? private var paginateForwardsTask: Task? + + private var pinnedEventsTimelineProvider: RoomTimelineProviderProtocol? { + didSet { + guard let pinnedEventsTimelineProvider else { + return + } + + buildPinnedEventContent(timelineItems: pinnedEventsTimelineProvider.itemProxies) + pinnedEventsTimelineProvider.updatePublisher + // When pinning or unpinning an item, the timeline might return empty for a short while, so we need to debounce it to prevent weird UI behaviours like the banner disappearing + .debounce(for: .milliseconds(100), scheduler: DispatchQueue.main) + .sink { [weak self] updatedItems, _ in + guard let self else { return } + buildPinnedEventContent(timelineItems: updatedItems) + } + .store(in: &cancellables) + } + } init(roomProxy: RoomProxyProtocol, focussedEventID: String? = nil, @@ -57,6 +76,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol mediaPlayerProvider: MediaPlayerProviderProtocol, voiceMessageMediaManager: VoiceMessageMediaManagerProtocol, userIndicatorController: UserIndicatorControllerProtocol, + networkMonitor: NetworkMonitorProtocol, appMediator: AppMediatorProtocol, appSettings: AppSettings, analyticsService: AnalyticsService) { @@ -67,6 +87,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol self.analyticsService = analyticsService self.userIndicatorController = userIndicatorController self.appMediator = appMediator + self.networkMonitor = networkMonitor pinnedEventStringBuilder = .pinnedEventStringBuilder(userID: roomProxy.ownUserID) let voiceMessageRecorder = VoiceMessageRecorder(audioRecorder: AudioRecorder(), mediaPlayerProvider: mediaPlayerProvider) @@ -127,22 +148,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } } - Task { - guard let pinnedEventsTimelineProvider = await roomProxy.pinnedEventsTimeline?.timelineProvider else { - return - } - - buildPinnedEventContent(timelineItems: pinnedEventsTimelineProvider.itemProxies) - - pinnedEventsTimelineProvider.updatePublisher - // When pinning or unpinning an item, the timeline might return empty for a short while, so we need to debounce it to prevent weird UI behaviours like the banner disappearing - .debounce(for: .milliseconds(100), scheduler: DispatchQueue.main) - .sink { [weak self] updatedItems, _ in - guard let self else { return } - buildPinnedEventContent(timelineItems: updatedItems) - } - .store(in: &cancellables) - } + setupPinnedEventsTimelineProviderIfNeeded() } // MARK: - Public @@ -454,17 +460,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } .store(in: &cancellables) - appSettings.$sharePresence - .weakAssign(to: \.state.showReadReceipts, on: self) - .store(in: &cancellables) - - appSettings.$viewSourceEnabled - .weakAssign(to: \.state.isViewSourceEnabled, on: self) - .store(in: &cancellables) - - appSettings.$pinningEnabled - .weakAssign(to: \.state.isPinningEnabled, on: self) - .store(in: &cancellables) + setupAppSettingsSubscriptions() roomProxy.membersPublisher .receive(on: DispatchQueue.main) @@ -511,6 +507,39 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } } .store(in: &cancellables) + + networkMonitor.reachabilityPublisher.sink { [weak self] networkState in + if networkState == .reachable { + self?.setupPinnedEventsTimelineProviderIfNeeded() + } + } + .store(in: &cancellables) + } + + private func setupAppSettingsSubscriptions() { + appSettings.$sharePresence + .weakAssign(to: \.state.showReadReceipts, on: self) + .store(in: &cancellables) + + appSettings.$viewSourceEnabled + .weakAssign(to: \.state.isViewSourceEnabled, on: self) + .store(in: &cancellables) + + appSettings.$pinningEnabled + .weakAssign(to: \.state.isPinningEnabled, on: self) + .store(in: &cancellables) + } + + private func setupPinnedEventsTimelineProviderIfNeeded() { + Task { + guard let pinnedEventsTimelineProvider = await roomProxy.pinnedEventsTimeline?.timelineProvider else { + return + } + + if self.pinnedEventsTimelineProvider == nil { + self.pinnedEventsTimelineProvider = pinnedEventsTimelineProvider + } + } } private func updatePinnedEventIDs() async { @@ -915,6 +944,7 @@ extension RoomScreenViewModel { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, + networkMonitor: ServiceLocator.shared.networkMonitor, appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics) diff --git a/ElementX/Sources/Screens/RoomScreen/View/ReadReceipts/ReadReceiptsSummaryView.swift b/ElementX/Sources/Screens/RoomScreen/View/ReadReceipts/ReadReceiptsSummaryView.swift index 165c8aae93..82433a1ecb 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/ReadReceipts/ReadReceiptsSummaryView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/ReadReceipts/ReadReceiptsSummaryView.swift @@ -58,6 +58,7 @@ struct ReadReceiptsSummaryView_Previews: PreviewProvider, TestablePreview { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: UserIndicatorControllerMock(), + networkMonitor: ServiceLocator.shared.networkMonitor, appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics) diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift index 1e2069d4b9..3243cd38e8 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift @@ -213,6 +213,7 @@ struct RoomScreen_Previews: PreviewProvider, TestablePreview { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, + networkMonitor: ServiceLocator.shared.networkMonitor, appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics) diff --git a/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineReadReceiptsView.swift b/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineReadReceiptsView.swift index e4933ee342..607eb7d610 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineReadReceiptsView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineReadReceiptsView.swift @@ -96,6 +96,7 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, + networkMonitor: ServiceLocator.shared.networkMonitor, appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics) diff --git a/ElementX/Sources/Screens/RoomScreen/View/Timeline/HighlightedTimelineItemModifier.swift b/ElementX/Sources/Screens/RoomScreen/View/Timeline/HighlightedTimelineItemModifier.swift index 3152077a99..d599a3289d 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Timeline/HighlightedTimelineItemModifier.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Timeline/HighlightedTimelineItemModifier.swift @@ -101,6 +101,7 @@ struct HighlightedTimelineItemTimeline_Previews: PreviewProvider { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, + networkMonitor: ServiceLocator.shared.networkMonitor, appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics) diff --git a/ElementX/Sources/Screens/RoomScreen/View/Timeline/TimelineView.swift b/ElementX/Sources/Screens/RoomScreen/View/Timeline/TimelineView.swift index 5a0b15423a..3778fb70ca 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Timeline/TimelineView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Timeline/TimelineView.swift @@ -86,7 +86,7 @@ struct TimelineView_Previews: PreviewProvider, TestablePreview { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, - appMediator: AppMediatorMock.default, + networkMonitor: ServiceLocator.shared.networkMonitor, appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics) diff --git a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-en-GB.1.png b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-en-GB.1.png index 92c1d53b36..df7eb8026b 100644 --- a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-en-GB.1.png +++ b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5eb75e371a78b17fad61215006b871310665bf8061772985d047facbccc5bae7 -size 129176 +oid sha256:30cbfcba6cb9794161ff7e6d0f587887dfb0e59abfeface5a167527bd6682712 +size 146870 diff --git a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-pseudo.1.png b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-pseudo.1.png index 7019c67c6f..c6be919ffb 100644 --- a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-pseudo.1.png +++ b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:745d8fdea278dc6ab55f39501384ecc5efc3899db233c52c797a8542f4885adc -size 154132 +oid sha256:e876895c2d5ef72b89695c1e587b1628344c579f07c2d24f0936b40bcba20f2c +size 181913 diff --git a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-en-GB.1.png b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-en-GB.1.png index 0aeb4f05d1..5a25173535 100644 --- a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-en-GB.1.png +++ b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82dc33b9de9b497388b91304afe2623e4fa5c9d5aff246a87b68c4d948a63a37 -size 76715 +oid sha256:4be5dbcc2bd36b126facff7c5bd017f1bc152d3f7035db2fc84fc379f2f90aba +size 91351 diff --git a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-pseudo.1.png b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-pseudo.1.png index 51383825dc..0929a35aff 100644 --- a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-pseudo.1.png +++ b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:48df9215b99d69572bf3c4395e4b13d0b34012691b98bba75c73fc35c3b0b6fb -size 85989 +oid sha256:3b9983bb1b027b6c8c3ceb180094f23334964f3ced43dfeacba90d79cd9d64b8 +size 107380 From b9dc34fdf0a93b5706bd574a2550b344b940dd04 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 5 Aug 2024 19:37:59 +0200 Subject: [PATCH 04/12] fix tests --- UnitTests/Sources/PillContextTests.swift | 3 +++ UnitTests/Sources/RoomScreenViewModelTests.swift | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/UnitTests/Sources/PillContextTests.swift b/UnitTests/Sources/PillContextTests.swift index 3ddbd4f78a..331d5ca3c2 100644 --- a/UnitTests/Sources/PillContextTests.swift +++ b/UnitTests/Sources/PillContextTests.swift @@ -32,6 +32,7 @@ class PillContextTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, + networkMonitor: ServiceLocator.shared.networkMonitor, appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics) @@ -60,6 +61,7 @@ class PillContextTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, + networkMonitor: ServiceLocator.shared.networkMonitor, appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics) @@ -81,6 +83,7 @@ class PillContextTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, + networkMonitor: ServiceLocator.shared.networkMonitor, appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics) diff --git a/UnitTests/Sources/RoomScreenViewModelTests.swift b/UnitTests/Sources/RoomScreenViewModelTests.swift index b547d928ed..851e37cd98 100644 --- a/UnitTests/Sources/RoomScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomScreenViewModelTests.swift @@ -351,6 +351,7 @@ class RoomScreenViewModelTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, + networkMonitor: ServiceLocator.shared.networkMonitor, appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics) @@ -375,6 +376,7 @@ class RoomScreenViewModelTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, + networkMonitor: ServiceLocator.shared.networkMonitor, appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics) @@ -398,7 +400,7 @@ class RoomScreenViewModelTests: XCTestCase { mediaProvider: MockMediaProvider(), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), - userIndicatorController: userIndicatorControllerMock, + userIndicatorController: userIndicatorControllerMock, networkMonitor: ServiceLocator.shared.networkMonitor, appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics) From e0e988ad36b01a2bc74b4ef76e82e0dc6ed3f872 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 5 Aug 2024 19:46:26 +0200 Subject: [PATCH 05/12] service locator setup for the network monitor mock --- ElementX.xcodeproj/project.pbxproj | 42 +++++++++---------- .../Sources/Mocks/NetworkMonitorMock.swift | 26 ++++++++++++ .../UITests/UITestsAppCoordinator.swift | 1 + .../UnitTests/UnitTestsAppCoordinator.swift | 1 + 4 files changed, 47 insertions(+), 23 deletions(-) create mode 100644 ElementX/Sources/Mocks/NetworkMonitorMock.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 7aeb7368af..ca1cbfc6e6 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 56; objects = { /* Begin PBXAggregateTarget section */ @@ -731,6 +731,7 @@ A6F345328CCC5C9B0DAE2257 /* LogViewerScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BB05221D7D941CC82DC8480 /* LogViewerScreenViewModel.swift */; }; A722F426FD81FC67706BB1E0 /* CustomLayoutLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42236480CF0431535EBE8387 /* CustomLayoutLabelStyle.swift */; }; A74438ED16F8683A4B793E6A /* AnalyticsSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BCE3FAF40932AC7C7639AC4 /* AnalyticsSettingsScreenViewModel.swift */; }; + A75B3BF22C61460B000CE956 /* NetworkMonitorMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A75B3BF12C61460B000CE956 /* NetworkMonitorMock.swift */; }; A7D48E44D485B143AADDB77D /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; }; A816F7087C495D85048AC50E /* RoomMemberDetailsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6E30BB748F3F480F077969 /* RoomMemberDetailsScreenModels.swift */; }; A851635B3255C6DC07034A12 /* RoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8108C8F0ACF6A7EB72D0117 /* RoomScreenCoordinator.swift */; }; @@ -1174,13 +1175,13 @@ 033DB41C51865A2E83174E87 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = ""; }; 035177BCD8E8308B098AC3C2 /* WindowManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManager.swift; sourceTree = ""; }; 0376C429FAB1687C3D905F3E /* MockCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCoder.swift; sourceTree = ""; }; - 0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */ = {isa = PBXFileReference; path = test_voice_message.m4a; sourceTree = ""; }; + 0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_voice_message.m4a; sourceTree = ""; }; 03DD998E523D4EC93C7ED703 /* RoomNotificationSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenViewModelProtocol.swift; sourceTree = ""; }; 03FABD73FD8086EFAB699F42 /* MediaUploadPreviewScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelTests.swift; sourceTree = ""; }; 044E501B8331B339874D1B96 /* CompoundIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompoundIcon.swift; sourceTree = ""; }; 045253F9967A535EE5B16691 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; 046C0D3F53B0B5EF0A1F5BEA /* RoomSummaryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryTests.swift; sourceTree = ""; }; - 048A21188AB19349D026BECD /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + 048A21188AB19349D026BECD /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 04BB8DDE245ED86C489BA983 /* AccessibilityIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityIdentifiers.swift; sourceTree = ""; }; 04DF593C3F7AF4B2FBAEB05D /* FileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManager.swift; sourceTree = ""; }; 0516C69708D5CBDE1A8E77EC /* RoomDirectorySearchProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchProxyProtocol.swift; sourceTree = ""; }; @@ -1240,7 +1241,7 @@ 127C8472672A5BA09EF1ACF8 /* CurrentValuePublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentValuePublisher.swift; sourceTree = ""; }; 128501375217576AF0FE3E92 /* RoomAttachmentPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomAttachmentPicker.swift; sourceTree = ""; }; 12F1E7F9C2BE8BB751037826 /* WaitlistScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitlistScreenCoordinator.swift; sourceTree = ""; }; - 1304D9191300873EADA52D6E /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; path = IntegrationTests.xctestplan; sourceTree = ""; }; + 1304D9191300873EADA52D6E /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = IntegrationTests.xctestplan; sourceTree = ""; }; 130ED565A078F7E0B59D9D25 /* UNTextInputNotificationResponse+Creator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UNTextInputNotificationResponse+Creator.swift"; sourceTree = ""; }; 136F80A613B55BDD071DCEA5 /* JoinRoomScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenModels.swift; sourceTree = ""; }; 13802897C7AFA360EA74C0B0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = ""; }; @@ -1331,7 +1332,7 @@ 25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItemProxy.swift; sourceTree = ""; }; 25F8664F1FB95AF3C4202478 /* PollFormScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenCoordinator.swift; sourceTree = ""; }; 260004737C573A56FA01E86E /* Encodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encodable.swift; sourceTree = ""; }; - 267BB1D5B08A9511F894CB57 /* PreviewTests.xctestplan */ = {isa = PBXFileReference; path = PreviewTests.xctestplan; sourceTree = ""; }; + 267BB1D5B08A9511F894CB57 /* PreviewTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = PreviewTests.xctestplan; sourceTree = ""; }; 26B0A96B8FE4849227945067 /* VoiceMessageRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRecorder.swift; sourceTree = ""; }; 26EAAB54C6CE91D64B69A9F8 /* AppLockServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockServiceProtocol.swift; sourceTree = ""; }; 2721D7B051F0159AA919DA05 /* RoomChangePermissionsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangePermissionsScreenViewModelProtocol.swift; sourceTree = ""; }; @@ -1396,7 +1397,7 @@ 3558A15CFB934F9229301527 /* RestorationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorationToken.swift; sourceTree = ""; }; 35AFCF4C05DEED04E3DB1A16 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; 35FA991289149D31F4286747 /* UserPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreference.swift; sourceTree = ""; }; - 36DA824791172B9821EACBED /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + 36DA824791172B9821EACBED /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberProxyMock.swift; sourceTree = ""; }; 376D941BF8BB294389C0DE24 /* MapTilerURLBuildersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerURLBuildersTests.swift; sourceTree = ""; }; 37A243E04B58DC6E41FDCD82 /* EmojiItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItem.swift; sourceTree = ""; }; @@ -1745,7 +1746,7 @@ 8D55702474F279D910D2D162 /* RoomStateEventStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomStateEventStringBuilder.swift; sourceTree = ""; }; 8D8169443E5AC5FF71BFB3DB /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; 8DC2C9E0E15C79BBDA80F0A2 /* TimelineStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyle.swift; sourceTree = ""; }; - 8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; path = UITests.xctestplan; sourceTree = ""; }; + 8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UITests.xctestplan; sourceTree = ""; }; 8E1584F8BCF407BB94F48F04 /* EncryptionResetPasswordScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetPasswordScreen.swift; sourceTree = ""; }; 8F21ED7205048668BEB44A38 /* AppActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppActivityView.swift; sourceTree = ""; }; 8F6210134203BE1F2DD5C679 /* RoomDirectoryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectoryCell.swift; sourceTree = ""; }; @@ -1840,6 +1841,7 @@ A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHGPostHogConfiguration.swift; sourceTree = ""; }; A6C11AD9813045E44F950410 /* ElementCallWidgetDriverProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallWidgetDriverProtocol.swift; sourceTree = ""; }; A73A07BAEDD74C48795A996A /* AsyncSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncSequence.swift; sourceTree = ""; }; + A75B3BF12C61460B000CE956 /* NetworkMonitorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitorMock.swift; sourceTree = ""; }; A7C4EA55DA62F9D0F984A2AE /* CollapsibleTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleTimelineItem.swift; sourceTree = ""; }; A7D452AF7B5F7E3A0A7DB54C /* SessionVerificationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenViewModelProtocol.swift; sourceTree = ""; }; A7E37072597F67C4DD8CC2DB /* ComposerDraftServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerDraftServiceProtocol.swift; sourceTree = ""; }; @@ -1909,7 +1911,7 @@ B53AC78E49A297AC1D72A7CF /* AppMediator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediator.swift; sourceTree = ""; }; B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadableAvatarImage.swift; sourceTree = ""; }; B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineView.swift; sourceTree = ""; }; - B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; path = ConfettiScene.scn; sourceTree = ""; }; + B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = ConfettiScene.scn; sourceTree = ""; }; B6311F21F911E23BE4DF51B4 /* ReadMarkerRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerRoomTimelineView.swift; sourceTree = ""; }; B63B69F9A2BC74DD40DC75C8 /* AdvancedSettingsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModel.swift; sourceTree = ""; }; B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetails.swift; sourceTree = ""; }; @@ -2022,7 +2024,7 @@ CE47A97726F0675DEE387BF9 /* TypingIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypingIndicatorView.swift; sourceTree = ""; }; CEE0E6043EFCF6FD2A341861 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = ""; }; CEE20623EB4A9B88FB29F2BA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/SAS.strings; sourceTree = ""; }; - CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; path = UnitTests.xctestplan; sourceTree = ""; }; + CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = ""; }; D071F86CD47582B9196C9D16 /* UserDiscoverySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoverySection.swift; sourceTree = ""; }; D086854995173E897F993C26 /* AdvancedSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModelProtocol.swift; sourceTree = ""; }; D09A267106B9585D3D0CFC0D /* ClientError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientError.swift; sourceTree = ""; }; @@ -2150,7 +2152,7 @@ ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRoomCell.swift; sourceTree = ""; }; ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemProtocol.swift; sourceTree = ""; }; ED33988DA4FD4FC666800106 /* SessionVerificationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenViewModel.swift; sourceTree = ""; }; - ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; path = message.caf; sourceTree = ""; }; + ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = message.caf; sourceTree = ""; }; ED60E4D2CD678E1EBF16F77A /* BlockedUsersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsersScreen.swift; sourceTree = ""; }; ED983D4DCA5AFA6E1ED96099 /* StateRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateRoomTimelineView.swift; sourceTree = ""; }; EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionViewModelTests.swift; sourceTree = ""; }; @@ -2173,7 +2175,7 @@ F174A5627CDB3CAF280D1880 /* EmojiPickerScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreenModels.swift; sourceTree = ""; }; F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProvider.swift; sourceTree = ""; }; F1B8500C152BC59445647DA8 /* UnsupportedRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsupportedRoomTimelineItem.swift; sourceTree = ""; }; - F2D513D2477B57F90E98EEC0 /* portrait_test_video.mp4 */ = {isa = PBXFileReference; path = portrait_test_video.mp4; sourceTree = ""; }; + F2D513D2477B57F90E98EEC0 /* portrait_test_video.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = portrait_test_video.mp4; sourceTree = ""; }; F2E4EF80DFB8FE7C4469B15D /* RoomDirectorySearchScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreen.swift; sourceTree = ""; }; F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixEntityRegexTests.swift; sourceTree = ""; }; F348B5F2C12F9D4F4B4D3884 /* VideoRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineItem.swift; sourceTree = ""; }; @@ -2773,6 +2775,7 @@ AAD01F7FC2BBAC7351948595 /* UserProfile+Mock.swift */, F4469F6AE311BDC439B3A5EC /* UserSessionMock.swift */, B23135B06B044CB811139D2F /* Generated */, + A75B3BF12C61460B000CE956 /* NetworkMonitorMock.swift */, ); path = Mocks; sourceTree = ""; @@ -6005,6 +6008,7 @@ 34433A509DFEC93579B3B35B /* AdvancedSettingsScreen.swift in Sources */, 4557192F5B15A8D9BB920232 /* AdvancedSettingsScreenCoordinator.swift in Sources */, 0A194F5E70B5A628C1BF4476 /* AdvancedSettingsScreenModels.swift in Sources */, + A75B3BF22C61460B000CE956 /* NetworkMonitorMock.swift in Sources */, B879446FD8E65A711EF8F9F7 /* AdvancedSettingsScreenViewModel.swift in Sources */, 62910B515BCB4B455E24D7C1 /* AdvancedSettingsScreenViewModelProtocol.swift in Sources */, 53C1E7F6A7D6409D89F36ED7 /* AggregatedReactionMock.swift in Sources */, @@ -7016,9 +7020,7 @@ "@executable_path/../../Frameworks", ); MARKETING_VERSION = "$(MARKETING_VERSION)"; - OTHER_SWIFT_FLAGS = ( - "-DIS_NSE", - ); + OTHER_SWIFT_FLAGS = "-DIS_NSE"; PRODUCT_BUNDLE_IDENTIFIER = "${BASE_BUNDLE_IDENTIFIER}.nse"; PRODUCT_DISPLAY_NAME = "$(APP_DISPLAY_NAME)"; PRODUCT_NAME = NSE; @@ -7067,9 +7069,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = "$(MARKETING_VERSION)"; - OTHER_SWIFT_FLAGS = ( - "-DIS_MAIN_APP", - ); + OTHER_SWIFT_FLAGS = "-DIS_MAIN_APP"; PILLS_UT_TYPE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).pills"; PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(APP_NAME)"; @@ -7095,9 +7095,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = "$(MARKETING_VERSION)"; - OTHER_SWIFT_FLAGS = ( - "-DIS_MAIN_APP", - ); + OTHER_SWIFT_FLAGS = "-DIS_MAIN_APP"; PILLS_UT_TYPE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).pills"; PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(APP_NAME)"; @@ -7342,9 +7340,7 @@ "@executable_path/../../Frameworks", ); MARKETING_VERSION = "$(MARKETING_VERSION)"; - OTHER_SWIFT_FLAGS = ( - "-DIS_NSE", - ); + OTHER_SWIFT_FLAGS = "-DIS_NSE"; PRODUCT_BUNDLE_IDENTIFIER = "${BASE_BUNDLE_IDENTIFIER}.nse"; PRODUCT_DISPLAY_NAME = "$(APP_DISPLAY_NAME)"; PRODUCT_NAME = NSE; diff --git a/ElementX/Sources/Mocks/NetworkMonitorMock.swift b/ElementX/Sources/Mocks/NetworkMonitorMock.swift new file mode 100644 index 0000000000..cb9cecf932 --- /dev/null +++ b/ElementX/Sources/Mocks/NetworkMonitorMock.swift @@ -0,0 +1,26 @@ +// +// Copyright 2024 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Combine +import Foundation + +extension NetworkMonitorMock { + static var `default`: NetworkMonitorMock { + let mock = NetworkMonitorMock() + mock.underlyingReachabilityPublisher = .init(.init(.reachable)) + return mock + } +} diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index 81eb828633..4583816ebe 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -47,6 +47,7 @@ class UITestsAppCoordinator: AppCoordinatorProtocol, SecureWindowManagerDelegate AppSettings.resetAllSettings() ServiceLocator.shared.register(appSettings: AppSettings()) ServiceLocator.shared.register(bugReportService: BugReportServiceMock()) + ServiceLocator.shared.register(networkMonitor: NetworkMonitorMock.default) let analyticsClient = AnalyticsClientMock() analyticsClient.isRunning = false diff --git a/ElementX/Sources/UnitTests/UnitTestsAppCoordinator.swift b/ElementX/Sources/UnitTests/UnitTestsAppCoordinator.swift index 689d672580..94f541bece 100644 --- a/ElementX/Sources/UnitTests/UnitTestsAppCoordinator.swift +++ b/ElementX/Sources/UnitTests/UnitTestsAppCoordinator.swift @@ -27,6 +27,7 @@ class UnitTestsAppCoordinator: AppCoordinatorProtocol { AppSettings.resetAllSettings() ServiceLocator.shared.register(appSettings: AppSettings()) ServiceLocator.shared.register(bugReportService: BugReportServiceMock()) + ServiceLocator.shared.register(networkMonitor: NetworkMonitorMock.default) let analyticsClient = AnalyticsClientMock() analyticsClient.isRunning = false From 3c51183b976cec48d4f5e6dde210998fcb84ad23 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 5 Aug 2024 20:33:32 +0200 Subject: [PATCH 06/12] fixed an issue that made the transition between the loading state and the loaded state not clean. --- ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift index 0f715c9d65..9c0bef25b3 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift @@ -417,7 +417,7 @@ enum PinnedEventsBannerState: Equatable { mutating func setPinnedEventContents(_ pinnedEventContents: OrderedDictionary) { switch self { case .loading: - self = .loaded(state: .init(pinnedEventContents: pinnedEventContents)) + self = .loaded(state: .init(pinnedEventContents: pinnedEventContents, selectedPinEventID: pinnedEventContents.keys.last)) case .loaded(var state): state.pinnedEventContents = pinnedEventContents self = .loaded(state: state) From 949acf9eeee63aa4f1e141072336572cf5966458 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 5 Aug 2024 20:40:46 +0200 Subject: [PATCH 07/12] better animation --- .../View/PinnedItemsBanner/PinnedItemsBannerView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift b/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift index 21b25df3b1..50d404ab79 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift @@ -41,6 +41,7 @@ struct PinnedItemsBannerView: View { .padding(.leading, 16) .background(Color.compound.bgCanvasDefault) .shadow(color: Color(red: 0.11, green: 0.11, blue: 0.13).opacity(0.1), radius: 12, x: 0, y: 4) + .animation(.elementDefault, value: state) } private var mainButton: some View { From 6edc3676a13fa71490bbf7dc0b022cce5c67d83a Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 6 Aug 2024 11:02:20 +0200 Subject: [PATCH 08/12] better handling --- .../RoomScreen/RoomScreenViewModel.swift | 27 ++++++++++++------- .../PinnedItemsBannerView.swift | 1 - 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index f612ef6e88..cf4a204d1f 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -50,6 +50,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol private var paginateBackwardsTask: Task? private var paginateForwardsTask: Task? + private var pinnedEventsTimelineProviderSetupTask: Task? private var pinnedEventsTimelineProvider: RoomTimelineProviderProtocol? { didSet { @@ -508,12 +509,14 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } .store(in: &cancellables) - networkMonitor.reachabilityPublisher.sink { [weak self] networkState in - if networkState == .reachable { - self?.setupPinnedEventsTimelineProviderIfNeeded() + networkMonitor.reachabilityPublisher + .receive(on: DispatchQueue.main) + .sink { [weak self] networkState in + if networkState == .reachable { + self?.setupPinnedEventsTimelineProviderIfNeeded() + } } - } - .store(in: &cancellables) + .store(in: &cancellables) } private func setupAppSettingsSubscriptions() { @@ -531,14 +534,20 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } private func setupPinnedEventsTimelineProviderIfNeeded() { - Task { + guard pinnedEventsTimelineProvider == nil, + pinnedEventsTimelineProviderSetupTask == nil else { + return + } + + pinnedEventsTimelineProviderSetupTask = Task { [weak self] in + guard let self else { return } + guard let pinnedEventsTimelineProvider = await roomProxy.pinnedEventsTimeline?.timelineProvider else { + pinnedEventsTimelineProviderSetupTask = nil return } - if self.pinnedEventsTimelineProvider == nil { - self.pinnedEventsTimelineProvider = pinnedEventsTimelineProvider - } + self.pinnedEventsTimelineProvider = pinnedEventsTimelineProvider } } diff --git a/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift b/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift index 50d404ab79..21b25df3b1 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift @@ -41,7 +41,6 @@ struct PinnedItemsBannerView: View { .padding(.leading, 16) .background(Color.compound.bgCanvasDefault) .shadow(color: Color(red: 0.11, green: 0.11, blue: 0.13).opacity(0.1), radius: 12, x: 0, y: 4) - .animation(.elementDefault, value: state) } private var mainButton: some View { From 992d538b17a9e897ef75bb497c7e558dbded81cf Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 6 Aug 2024 11:09:47 +0200 Subject: [PATCH 09/12] formatting --- UnitTests/Sources/RoomScreenViewModelTests.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/UnitTests/Sources/RoomScreenViewModelTests.swift b/UnitTests/Sources/RoomScreenViewModelTests.swift index 851e37cd98..1ca48bd744 100644 --- a/UnitTests/Sources/RoomScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomScreenViewModelTests.swift @@ -400,7 +400,8 @@ class RoomScreenViewModelTests: XCTestCase { mediaProvider: MockMediaProvider(), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), - userIndicatorController: userIndicatorControllerMock, networkMonitor: ServiceLocator.shared.networkMonitor, + userIndicatorController: userIndicatorControllerMock, + networkMonitor: ServiceLocator.shared.networkMonitor, appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics) From 2ca04ea61f3169a305633f3b0d2aae030c5e966c Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 6 Aug 2024 11:51:51 +0200 Subject: [PATCH 10/12] pr suggestions --- .../Localizations/en.lproj/Localizable.strings | 2 +- ElementX/Sources/Generated/Strings.swift | 2 +- .../Screens/RoomScreen/RoomScreenModels.swift | 12 ++++++++++++ .../PinnedItemsBanner/PinnedItemsBannerView.swift | 11 +---------- .../RoomScreen/View/Timeline/TimelineView.swift | 3 ++- .../RoomSummary/RoomMessageEventStringBuilder.swift | 12 ++++++------ 6 files changed, 23 insertions(+), 19 deletions(-) diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 180d85caf1..fe2d2b9290 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -318,7 +318,7 @@ "screen_room_mentions_at_room_subtitle" = "Notify the whole room"; "screen_room_pinned_banner_indicator" = "%1$@ of %2$@"; "screen_room_pinned_banner_indicator_description" = "%1$@ Pinned messages"; -"screen_room_pinned_banner_loading_description" = "Loading message..."; +"screen_room_pinned_banner_loading_description" = "Loading messageā€¦"; "screen_room_pinned_banner_view_all_button_title" = "View All"; "screen_account_provider_change" = "Change account provider"; "screen_account_provider_form_hint" = "Homeserver address"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index ee77d0e8b2..e798ad2619 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -1675,7 +1675,7 @@ internal enum L10n { internal static func screenRoomPinnedBannerIndicatorDescription(_ p1: Any) -> String { return L10n.tr("Localizable", "screen_room_pinned_banner_indicator_description", String(describing: p1)) } - /// Loading message... + /// Loading messageā€¦ internal static var screenRoomPinnedBannerLoadingDescription: String { return L10n.tr("Localizable", "screen_room_pinned_banner_loading_description") } /// View All internal static var screenRoomPinnedBannerViewAllButtonTitle: String { return L10n.tr("Localizable", "screen_room_pinned_banner_view_all_button_title") } diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift index 9c0bef25b3..19494fafec 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift @@ -306,6 +306,7 @@ struct PinnedEventsState: Equatable { var pinnedEventContents: OrderedDictionary = [:] { didSet { if selectedPinEventID == nil, !pinnedEventContents.keys.isEmpty { + // The default selected event should always be the last one. selectedPinEventID = pinnedEventContents.keys.last } else if pinnedEventContents.isEmpty { selectedPinEventID = nil @@ -390,10 +391,20 @@ enum PinnedEventsBannerState: Equatable { case .loaded(let state): return state.selectedPinIndex case .loading(let numbersOfEvents): + // We always want the index to be the last one when loading, since is the default one. return numbersOfEvents - 1 } } + var displayedMessage: AttributedString { + switch self { + case .loading: + return AttributedString(L10n.screenRoomPinnedBannerLoadingDescription) + case .loaded(let state): + return state.selectedPinContent + } + } + var bannerIndicatorDescription: AttributedString { let index = selectedPinIndex + 1 let boldPlaceholder = "{bold}" @@ -417,6 +428,7 @@ enum PinnedEventsBannerState: Equatable { mutating func setPinnedEventContents(_ pinnedEventContents: OrderedDictionary) { switch self { case .loading: + // The default selected event should always be the last one. self = .loaded(state: .init(pinnedEventContents: pinnedEventContents, selectedPinEventID: pinnedEventContents.keys.last)) case .loaded(var state): state.pinnedEventContents = pinnedEventContents diff --git a/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift b/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift index 21b25df3b1..4af94567d6 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift @@ -23,15 +23,6 @@ struct PinnedItemsBannerView: View { let onMainButtonTap: () -> Void let onViewAllButtonTap: () -> Void - private var displayedMessage: AttributedString { - switch state { - case .loading: - return AttributedString(L10n.screenRoomPinnedBannerLoadingDescription) - case .loaded(let state): - return state.selectedPinContent - } - } - var body: some View { HStack(spacing: 0) { mainButton @@ -84,7 +75,7 @@ struct PinnedItemsBannerView: View { .font(.compound.bodySM) .foregroundColor(.compound.textActionAccent) .lineLimit(1) - Text(displayedMessage) + Text(state.displayedMessage) .font(.compound.bodyMD) .foregroundColor(.compound.textPrimary) .lineLimit(1) diff --git a/ElementX/Sources/Screens/RoomScreen/View/Timeline/TimelineView.swift b/ElementX/Sources/Screens/RoomScreen/View/Timeline/TimelineView.swift index 3778fb70ca..d415bb4c8b 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Timeline/TimelineView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Timeline/TimelineView.swift @@ -86,7 +86,8 @@ struct TimelineView_Previews: PreviewProvider, TestablePreview { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, - networkMonitor: ServiceLocator.shared.networkMonitor, appMediator: AppMediatorMock.default, + networkMonitor: ServiceLocator.shared.networkMonitor, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics) diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomMessageEventStringBuilder.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomMessageEventStringBuilder.swift index 37bba51ca7..93c430f931 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomMessageEventStringBuilder.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomMessageEventStringBuilder.swift @@ -20,7 +20,7 @@ import MatrixRustSDK struct RoomMessageEventStringBuilder { enum Prefix { case senderName - case type + case messageType case none } @@ -41,19 +41,19 @@ struct RoomMessageEventStringBuilder { case .audio(content: let content): let isVoiceMessage = content.voice != nil var content = AttributedString(isVoiceMessage ? L10n.commonVoiceMessage : L10n.commonAudio) - if prefix == .type { + if prefix == .messageType { content.bold() } message = content case .image(let content): - message = prefix == .type ? prefix(AttributedString(content.body), with: L10n.commonImage) : AttributedString("\(L10n.commonImage) - \(content.body)") + message = prefix == .messageType ? prefix(AttributedString(content.body), with: L10n.commonImage) : AttributedString("\(L10n.commonImage) - \(content.body)") case .video(let content): - message = prefix == .type ? prefix(AttributedString(content.body), with: L10n.commonVideo) : AttributedString("\(L10n.commonVideo) - \(content.body)") + message = prefix == .messageType ? prefix(AttributedString(content.body), with: L10n.commonVideo) : AttributedString("\(L10n.commonVideo) - \(content.body)") case .file(let content): - message = prefix == .type ? prefix(AttributedString(content.body), with: L10n.commonFile) : AttributedString("\(L10n.commonFile) - \(content.body)") + message = prefix == .messageType ? prefix(AttributedString(content.body), with: L10n.commonFile) : AttributedString("\(L10n.commonFile) - \(content.body)") case .location: var content = AttributedString(L10n.commonSharedLocation) - if prefix == .type { + if prefix == .messageType { content.bold() } message = content From b909454911bf2f6dd1b7e97f697d6802c785c238 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 6 Aug 2024 12:44:17 +0200 Subject: [PATCH 11/12] added tests --- ElementX.xcodeproj/project.pbxproj | 4 + .../Screens/RoomScreen/RoomScreenModels.swift | 2 +- .../RoomSummary/RoomEventStringBuilder.swift | 6 +- .../PinnedEventsBannerStateTests.swift | 133 ++++++++++++++++++ 4 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 UnitTests/Sources/PinnedEventsBannerStateTests.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index ca1cbfc6e6..0d60d62c9d 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -731,6 +731,7 @@ A6F345328CCC5C9B0DAE2257 /* LogViewerScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BB05221D7D941CC82DC8480 /* LogViewerScreenViewModel.swift */; }; A722F426FD81FC67706BB1E0 /* CustomLayoutLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42236480CF0431535EBE8387 /* CustomLayoutLabelStyle.swift */; }; A74438ED16F8683A4B793E6A /* AnalyticsSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BCE3FAF40932AC7C7639AC4 /* AnalyticsSettingsScreenViewModel.swift */; }; + A7580E042C622A5300A0991D /* PinnedEventsBannerStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7580E032C622A5300A0991D /* PinnedEventsBannerStateTests.swift */; }; A75B3BF22C61460B000CE956 /* NetworkMonitorMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A75B3BF12C61460B000CE956 /* NetworkMonitorMock.swift */; }; A7D48E44D485B143AADDB77D /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; }; A816F7087C495D85048AC50E /* RoomMemberDetailsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6E30BB748F3F480F077969 /* RoomMemberDetailsScreenModels.swift */; }; @@ -1841,6 +1842,7 @@ A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHGPostHogConfiguration.swift; sourceTree = ""; }; A6C11AD9813045E44F950410 /* ElementCallWidgetDriverProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallWidgetDriverProtocol.swift; sourceTree = ""; }; A73A07BAEDD74C48795A996A /* AsyncSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncSequence.swift; sourceTree = ""; }; + A7580E032C622A5300A0991D /* PinnedEventsBannerStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedEventsBannerStateTests.swift; sourceTree = ""; }; A75B3BF12C61460B000CE956 /* NetworkMonitorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitorMock.swift; sourceTree = ""; }; A7C4EA55DA62F9D0F984A2AE /* CollapsibleTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleTimelineItem.swift; sourceTree = ""; }; A7D452AF7B5F7E3A0A7DB54C /* SessionVerificationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenViewModelProtocol.swift; sourceTree = ""; }; @@ -3741,6 +3743,7 @@ 7583EAC171059A86B767209F /* MediaProvider */, 7DBC911559934065993A5FF4 /* NotificationManager */, 1C62F5382CC9D9F7DCEC344A /* UserDiscoveryService */, + A7580E032C622A5300A0991D /* PinnedEventsBannerStateTests.swift */, ); path = Sources; sourceTree = ""; @@ -5982,6 +5985,7 @@ 04F17DE71A50206336749BAC /* UserPreferenceTests.swift in Sources */, 73F547BEB41D3DAFAAF6E0AF /* UserProfileScreenViewModelTests.swift in Sources */, 627139A3D79F032BA81E3A53 /* UserSessionFlowCoordinatorTests.swift in Sources */, + A7580E042C622A5300A0991D /* PinnedEventsBannerStateTests.swift in Sources */, 81A7C020CB5F6232242A8414 /* UserSessionTests.swift in Sources */, 21AFEFB8CEFE56A3811A1F5B /* VoiceMessageCacheTests.swift in Sources */, 44BDD670FF9095ACE240A3A2 /* VoiceMessageMediaManagerTests.swift in Sources */, diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift index 19494fafec..41e46be6f5 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift @@ -311,7 +311,7 @@ struct PinnedEventsState: Equatable { } else if pinnedEventContents.isEmpty { selectedPinEventID = nil } else if let selectedPinEventID, !pinnedEventContents.keys.set.contains(selectedPinEventID) { - self.selectedPinEventID = pinnedEventContents.firstNonNil { $0.key } + self.selectedPinEventID = pinnedEventContents.keys.last } } } diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift index e47021a02c..fcf4fdb12f 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift @@ -42,7 +42,7 @@ struct RoomEventStringBuilder { case .redactedMessage: return prefix(L10n.commonMessageRemoved, with: displayName) case .sticker: - if messageEventStringBuilder.prefix == .type { + if messageEventStringBuilder.prefix == .messageType { var string = AttributedString(L10n.commonSticker) string.bold() return string @@ -75,7 +75,7 @@ struct RoomEventStringBuilder { memberIsYou: isOutgoing) .map(AttributedString.init) case .poll(let question, _, _, _, _, _, _): - if messageEventStringBuilder.prefix == .type { + if messageEventStringBuilder.prefix == .messageType { let questionPlaceholder = "{question}" var finalString = AttributedString(L10n.commonPollSummary(questionPlaceholder)) finalString.bold() @@ -108,7 +108,7 @@ struct RoomEventStringBuilder { RoomEventStringBuilder(stateEventStringBuilder: .init(userID: userID, shouldDisambiguateDisplayNames: false), messageEventStringBuilder: .init(attributedStringBuilder: AttributedStringBuilder(cacheKey: "pinnedEvents", mentionBuilder: PlainMentionBuilder()), - prefix: .type), + prefix: .messageType), shouldDisambiguateDisplayNames: false, shouldPrefixSenderName: false) } diff --git a/UnitTests/Sources/PinnedEventsBannerStateTests.swift b/UnitTests/Sources/PinnedEventsBannerStateTests.swift new file mode 100644 index 0000000000..bddb32baf6 --- /dev/null +++ b/UnitTests/Sources/PinnedEventsBannerStateTests.swift @@ -0,0 +1,133 @@ +// +// Copyright 2024 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest + +@testable import ElementX + +class PinnedEventsBannerStateTests: XCTestCase { + func testEmpty() { + var state = PinnedEventsBannerState.loading(numbersOfEvents: 0) + XCTAssertTrue(state.isEmpty) + + state = .loaded(state: .init()) + XCTAssertTrue(state.isEmpty) + } + + func testLoading() { + let originalState = PinnedEventsBannerState.loading(numbersOfEvents: 5) + + var state = originalState + // This should not affect the state when loading + state.nextPin() + XCTAssertEqual(state, originalState) + + XCTAssertTrue(state.isLoading) + XCTAssertFalse(state.isEmpty) + XCTAssertNil(state.selectedPinEventID) + XCTAssertEqual(state.displayedMessage.string, L10n.screenRoomPinnedBannerLoadingDescription) + XCTAssertEqual(state.selectedPinIndex, 4) + XCTAssertEqual(state.count, 5) + XCTAssertEqual(state.bannerIndicatorDescription.string, L10n.screenRoomPinnedBannerIndicatorDescription(L10n.screenRoomPinnedBannerIndicator(5, 5))) + } + + func testLoadingToLoaded() { + var state = PinnedEventsBannerState.loading(numbersOfEvents: 2) + XCTAssertTrue(state.isLoading) + state.setPinnedEventContents(["1": "test1", "2": "test2"]) + XCTAssertEqual(state, .loaded(state: .init(pinnedEventContents: ["1": "test1", "2": "test2"], selectedPinEventID: "2"))) + XCTAssertFalse(state.isLoading) + } + + func testLoaded() { + let state = PinnedEventsBannerState.loaded(state: .init(pinnedEventContents: ["1": "test1", "2": "test2"], selectedPinEventID: "2")) + XCTAssertFalse(state.isLoading) + XCTAssertFalse(state.isEmpty) + XCTAssertEqual(state.selectedPinEventID, "2") + XCTAssertEqual(state.displayedMessage.string, "test2") + XCTAssertEqual(state.selectedPinIndex, 1) + XCTAssertEqual(state.count, 2) + XCTAssertEqual(state.bannerIndicatorDescription.string, L10n.screenRoomPinnedBannerIndicatorDescription(L10n.screenRoomPinnedBannerIndicator(2, 2))) + } + + func testNextPin() { + var state = PinnedEventsBannerState.loaded(state: .init(pinnedEventContents: ["1": "test1", "2": "test2", "3": "test3"], selectedPinEventID: "3")) + XCTAssertEqual(state.selectedPinEventID, "3") + XCTAssertEqual(state.selectedPinIndex, 2) + XCTAssertEqual(state.displayedMessage.string, "test3") + + state.nextPin() + XCTAssertEqual(state.selectedPinEventID, "1") + XCTAssertEqual(state.selectedPinIndex, 0) + XCTAssertEqual(state.displayedMessage.string, "test1") + + state.nextPin() + XCTAssertEqual(state.selectedPinEventID, "2") + XCTAssertEqual(state.selectedPinIndex, 1) + XCTAssertEqual(state.displayedMessage.string, "test2") + } + + func testSetContent() { + var state = PinnedEventsBannerState.loaded(state: .init(pinnedEventContents: ["1": "test1", "2": "test2", "3": "test3", "4": "test4"], selectedPinEventID: "2")) + XCTAssertEqual(state.selectedPinEventID, "2") + XCTAssertEqual(state.selectedPinIndex, 1) + XCTAssertEqual(state.displayedMessage.string, "test2") + XCTAssertEqual(state.count, 4) + XCTAssertFalse(state.isEmpty) + + // let's remove the selected item + state.setPinnedEventContents(["1": "test1", "3": "test3", "4": "test4"]) + // new selected item is the new latest + XCTAssertEqual(state.selectedPinEventID, "4") + XCTAssertEqual(state.selectedPinIndex, 2) + XCTAssertEqual(state.displayedMessage.string, "test4") + XCTAssertEqual(state.count, 3) + XCTAssertFalse(state.isEmpty) + + // let's add a new item at the top + state.setPinnedEventContents(["0": "test0", "1": "test1", "3": "test3", "4": "test4"]) + // selected item doesn't change + XCTAssertEqual(state.selectedPinEventID, "4") + // but the index is updated + XCTAssertEqual(state.selectedPinIndex, 3) + XCTAssertEqual(state.displayedMessage.string, "test4") + XCTAssertEqual(state.count, 4) + XCTAssertFalse(state.isEmpty) + + // let's add a new item at the bottom + state.setPinnedEventContents(["0": "test0", "1": "test1", "3": "test3", "4": "test4", "5": "test5"]) + // selected item doesn't change + XCTAssertEqual(state.selectedPinEventID, "4") + // and index stays the same + XCTAssertEqual(state.selectedPinIndex, 3) + XCTAssertEqual(state.displayedMessage.string, "test4") + XCTAssertEqual(state.count, 5) + XCTAssertFalse(state.isEmpty) + + // set to tempty + state.setPinnedEventContents([:]) + XCTAssertTrue(state.isEmpty) + XCTAssertNil(state.selectedPinEventID) + + // set to one item + state.setPinnedEventContents(["6": "test6", "7": "test7"]) + XCTAssertEqual(state.selectedPinEventID, "7") + XCTAssertEqual(state.selectedPinIndex, 1) + XCTAssertEqual(state.displayedMessage.string, "test7") + XCTAssertEqual(state.count, 2) + XCTAssertFalse(state.isEmpty) + } +} From d373647a7ca3a02b7b635445d4010ed508becc3f Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 6 Aug 2024 15:46:00 +0200 Subject: [PATCH 12/12] updated tests --- .../View/PinnedItemsBanner/PinnedItemsBannerView.swift | 6 +++--- .../test_pinnedItemsBannerView-iPad-en-GB.1.png | 4 ++-- .../test_pinnedItemsBannerView-iPad-pseudo.1.png | 4 ++-- .../test_pinnedItemsBannerView-iPhone-15-en-GB.1.png | 4 ++-- .../test_pinnedItemsBannerView-iPhone-15-pseudo.1.png | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift b/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift index 4af94567d6..8cb77c8b0f 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/PinnedItemsBanner/PinnedItemsBannerView.swift @@ -87,10 +87,10 @@ struct PinnedItemsBannerView_Previews: PreviewProvider, TestablePreview { static var attributedContent: AttributedString { var boldPart = AttributedString("Image:") boldPart.bold() - var final = boldPart + " content.png" + var finalString = boldPart + " content.png" // This should be ignored when presented - final.font = .headline - return final + finalString.font = .headline + return finalString } static var previews: some View { diff --git a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-en-GB.1.png b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-en-GB.1.png index df7eb8026b..22b97c881f 100644 --- a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-en-GB.1.png +++ b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30cbfcba6cb9794161ff7e6d0f587887dfb0e59abfeface5a167527bd6682712 -size 146870 +oid sha256:52a543349c38a46a7abf42d9f5b83aed47b40621e358fe63b98487acff03b461 +size 147075 diff --git a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-pseudo.1.png b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-pseudo.1.png index c6be919ffb..40030d446f 100644 --- a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-pseudo.1.png +++ b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e876895c2d5ef72b89695c1e587b1628344c579f07c2d24f0936b40bcba20f2c -size 181913 +oid sha256:131dadbb128453abb744bd49c2cd592bbd9f0f5bdee5ba6ea71f541a10ee70ee +size 182373 diff --git a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-en-GB.1.png b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-en-GB.1.png index 5a25173535..59dcea9a6a 100644 --- a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-en-GB.1.png +++ b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4be5dbcc2bd36b126facff7c5bd017f1bc152d3f7035db2fc84fc379f2f90aba -size 91351 +oid sha256:ade81f08af5344aab81fdebbffe27a4b04976ce8e3cc8f397d5328c51998ad09 +size 91413 diff --git a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-pseudo.1.png b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-pseudo.1.png index 0929a35aff..fcd8c4da61 100644 --- a/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-pseudo.1.png +++ b/PreviewTests/__Snapshots__/PreviewTests/test_pinnedItemsBannerView-iPhone-15-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b9983bb1b027b6c8c3ceb180094f23334964f3ced43dfeacba90d79cd9d64b8 -size 107380 +oid sha256:919e3df86ce263d06f479c701b6981a66b0d6217e13cf3cff12445b9c6a24d77 +size 106938