diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 0195ff3db..b253d6bf8 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -9406,7 +9406,7 @@ repositoryURL = "https://github.com/element-hq/compound-ios"; requirement = { kind = revision; - revision = 8d7ca2e413026735b724044847ff1ae3b40563f0; + revision = 4ad0bd0acd825a905bb4955b32979fe34cc538eb; }; }; F76A08D0EA29A07A54F4EB4D /* XCRemoteSwiftPackageReference "swift-collections" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 5abc9d881..77fa08ec5 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -15,7 +15,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/compound-ios", "state" : { - "revision" : "8d7ca2e413026735b724044847ff1ae3b40563f0" + "revision" : "4ad0bd0acd825a905bb4955b32979fe34cc538eb" } }, { @@ -275,8 +275,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-snapshot-testing", "state" : { - "revision" : "37230a37e83f1b7023be08e1b1a2603fcb1567fb", - "version" : "1.18.4" + "revision" : "1be8144023c367c5de701a6313ed29a3a10bf59b", + "version" : "1.18.3" } }, { diff --git a/ElementX/Sources/Other/SwiftUI/Animation/ShimmerModifier.swift b/ElementX/Sources/Other/SwiftUI/Animation/ShimmerModifier.swift index 247d7da56..25e49d5c6 100644 --- a/ElementX/Sources/Other/SwiftUI/Animation/ShimmerModifier.swift +++ b/ElementX/Sources/Other/SwiftUI/Animation/ShimmerModifier.swift @@ -67,17 +67,10 @@ struct ShimmerModifier: ViewModifier { } struct ShimmerOverlay_Previews: PreviewProvider, TestablePreview { - static let viewModel = HomeScreenViewModel(userSession: UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "")))), - selectedRoomPublisher: CurrentValueSubject(nil).asCurrentValuePublisher(), - appSettings: ServiceLocator.shared.settings, - analyticsService: ServiceLocator.shared.analytics, - notificationManager: NotificationManagerMock(), - userIndicatorController: ServiceLocator.shared.userIndicatorController) - static var previews: some View { VStack(spacing: 0) { ForEach(0...8, id: \.self) { _ in - HomeScreenRoomCell(room: .placeholder(), context: viewModel.context, isSelected: false) + HomeScreenRoomCell(room: .placeholder(), isSelected: false, mediaProvider: MediaProviderMock(configuration: .init())) { _ in } } } .redacted(reason: .placeholder) diff --git a/ElementX/Sources/Screens/HomeScreen/View/BloomModifier.swift b/ElementX/Sources/Screens/HomeScreen/View/BloomModifier.swift index 5f30b52c2..b1015ff1e 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/BloomModifier.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/BloomModifier.swift @@ -17,10 +17,12 @@ extension View { } private struct BloomModifier: ViewModifier { + @Environment(\.colorScheme) private var colorScheme + @State private var standardAppearance = UINavigationBarAppearance() @State private var scrollEdgeAppearance = UINavigationBarAppearance() - @State private var bloomGradientImage: UIImage? + @State private var bloom = Bloom() func body(content: Content) -> some View { content @@ -28,33 +30,39 @@ private struct BloomModifier: ViewModifier { } private func configureBloom(controller: UIViewController) { - guard controller.navigationItem.standardAppearance != standardAppearance, - controller.navigationItem.scrollEdgeAppearance != scrollEdgeAppearance else { + if controller.navigationItem.standardAppearance == standardAppearance, + controller.navigationItem.scrollEdgeAppearance == scrollEdgeAppearance, + canUse(bloom) { return } - let image = makeBloomImage() + let bloom = makeBloom() standardAppearance.configureWithDefaultBackground() - standardAppearance.backgroundImage = image + standardAppearance.backgroundImage = bloom.image standardAppearance.backgroundImageContentMode = .scaleToFill controller.navigationItem.standardAppearance = standardAppearance scrollEdgeAppearance.configureWithTransparentBackground() - scrollEdgeAppearance.backgroundImage = image + scrollEdgeAppearance.backgroundImage = bloom.image scrollEdgeAppearance.backgroundImageContentMode = .scaleToFill scrollEdgeAppearance.backgroundColor = .compound.bgCanvasDefault controller.navigationItem.scrollEdgeAppearance = scrollEdgeAppearance } - private func makeBloomImage() -> UIImage? { - if let bloomGradientImage { - return bloomGradientImage + private func makeBloom() -> Bloom { + if bloom.image != nil, canUse(bloom) { + return bloom } - let newImage = ImageRenderer(content: bloomGradient).uiImage - Task { bloomGradientImage = newImage } - return newImage + // There's a bug somewhere when rendering in dark mode (which we've mistakenly not been doing) + // which results in the first 5 stops not having any alpha, only the last one… + let newImage = ImageRenderer(content: bloomGradient /* .colorScheme(colorScheme) */ ).uiImage + + bloom.image = newImage + bloom.colorScheme = colorScheme + bloom.baseColor = .compound.gradientSubtleStop1 + return bloom } private var bloomGradient: some View { @@ -64,4 +72,18 @@ private struct BloomModifier: ViewModifier { .ignoresSafeArea(edges: .all) .frame(width: 256, height: 256) } + + private func canUse(_ bloom: Bloom) -> Bool { + // Don't check for a nil image in here, there's no point re-rendering over and over if the render fails. + bloom.colorScheme == colorScheme && bloom.baseColor == .compound.gradientSubtleStop1 + } + + // This is a class to avoid a "Modifying state during view update" warning when storing + // the result on the same run-loop - we want to avoid dispatching that to the next loop as + // that can result in further (unnecessary) renders being made. + class Bloom { + var image: UIImage? + var colorScheme: ColorScheme? + var baseColor: Color? + } } diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift index edff821ce..7e5dba6bc 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift @@ -27,7 +27,7 @@ struct HomeScreenContent: View { case .skeletons: LazyVStack(spacing: 0) { ForEach(context.viewState.visibleRooms) { room in - HomeScreenRoomCell(room: room, context: context, isSelected: false) + HomeScreenRoomCell(room: room, isSelected: false, mediaProvider: context.mediaProvider, action: context.send) .redacted(reason: .placeholder) .shimmer() // Putting this directly on the LazyVStack creates an accordion animation on iOS 16. } diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift index d7cdebee3..d9926f368 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift @@ -14,8 +14,9 @@ struct HomeScreenRoomCell: View { @Environment(\.redactionReasons) private var redactionReasons let room: HomeScreenRoom - let context: HomeScreenViewModel.Context let isSelected: Bool + let mediaProvider: MediaProviderProtocol! + let action: (HomeScreenViewAction) -> Void private let verticalInsets = 12.0 private let horizontalInsets = 16.0 @@ -23,7 +24,7 @@ struct HomeScreenRoomCell: View { var body: some View { Button { if let roomID = room.roomID { - context.send(viewAction: .selectRoom(roomIdentifier: roomID)) + action(.selectRoom(roomIdentifier: roomID)) } } label: { HStack(spacing: 16.0) { @@ -51,7 +52,7 @@ struct HomeScreenRoomCell: View { if dynamicTypeSize < .accessibility3 { RoomAvatarImage(avatar: room.avatar, avatarSize: .room(on: .chats), - mediaProvider: context.mediaProvider) + mediaProvider: mediaProvider) .dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1) .accessibilityHidden(true) } @@ -167,27 +168,25 @@ private extension View { struct HomeScreenRoomCell_Previews: PreviewProvider, TestablePreview { static let summaryProviderGeneric = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))) - static let viewModelGeneric = makeViewModel(roomSummaryProvider: summaryProviderGeneric) static let genericRooms = summaryProviderGeneric.roomListPublisher.value.compactMap(mockRoom) static let summaryProviderForNotificationsState = RoomSummaryProviderMock(.init(state: .loaded(.mockRoomsWithNotificationsState))) - static let viewModelForNotificationsState = makeViewModel(roomSummaryProvider: summaryProviderForNotificationsState) static let notificationsStateRooms = summaryProviderForNotificationsState.roomListPublisher.value.compactMap(mockRoom) static var previews: some View { VStack(spacing: 0) { ForEach(genericRooms) { room in - HomeScreenRoomCell(room: room, context: viewModelGeneric.context, isSelected: false) + HomeScreenRoomCell(room: room, isSelected: false, mediaProvider: MediaProviderMock(configuration: .init())) { _ in } } - HomeScreenRoomCell(room: .placeholder(), context: viewModelGeneric.context, isSelected: false) + HomeScreenRoomCell(room: .placeholder(), isSelected: false, mediaProvider: MediaProviderMock(configuration: .init())) { _ in } .redacted(reason: .placeholder) } .previewDisplayName("Generic") VStack(spacing: 0) { ForEach(notificationsStateRooms) { room in - HomeScreenRoomCell(room: room, context: viewModelForNotificationsState.context, isSelected: false) + HomeScreenRoomCell(room: room, isSelected: false, mediaProvider: MediaProviderMock(configuration: .init())) { _ in } } } .previewLayout(.sizeThatFits) diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift index 525fea3ef..7a1922879 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift @@ -24,7 +24,7 @@ struct HomeScreenRoomList: View { ForEach(context.viewState.visibleRooms) { room in switch room.type { case .placeholder: - HomeScreenRoomCell(room: room, context: context, isSelected: false) + HomeScreenRoomCell(room: room, isSelected: false, mediaProvider: context.mediaProvider, action: context.send) .redacted(reason: .placeholder) case .invite: HomeScreenInviteCell(room: room, context: context, hideInviteAvatars: context.viewState.hideInviteAvatars) @@ -33,7 +33,7 @@ struct HomeScreenRoomList: View { case .room: let isSelected = context.viewState.selectedRoomID == room.id - HomeScreenRoomCell(room: room, context: context, isSelected: isSelected) + HomeScreenRoomCell(room: room, isSelected: isSelected, mediaProvider: context.mediaProvider, action: context.send) .contextMenu { if room.badges.isDotShown { Button { diff --git a/project.yml b/project.yml index d2f10b34b..850833934 100644 --- a/project.yml +++ b/project.yml @@ -72,7 +72,7 @@ packages: # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios - revision: 8d7ca2e413026735b724044847ff1ae3b40563f0 + revision: 4ad0bd0acd825a905bb4955b32979fe34cc538eb # path: ../compound-ios AnalyticsEvents: url: https://github.com/matrix-org/matrix-analytics-events