diff --git a/IceCubesApp/App/IceCubesApp.swift b/IceCubesApp/App/IceCubesApp.swift index b62f00d0..48318f68 100644 --- a/IceCubesApp/App/IceCubesApp.swift +++ b/IceCubesApp/App/IceCubesApp.swift @@ -135,9 +135,7 @@ struct IceCubesApp: App { userPreferences.showiPadSecondaryColumn { Divider().edgesIgnoringSafeArea(.all) - NotificationsTab(popToRootTab: $popToRootTab, lockedType: nil) - .environment(\.isSecondaryColumn, true) - .frame(maxWidth: 360) + notificationsSecondaryColumn } } } @@ -145,6 +143,13 @@ struct IceCubesApp: App { sideBarLoadedTabs.removeAll() } } + + private var notificationsSecondaryColumn: some View { + NotificationsTab(popToRootTab: $popToRootTab, lockedType: nil) + .environment(\.isSecondaryColumn, true) + .frame(maxWidth: 360) + .id(appAccountsManager.currentAccount.id) + } private var tabBarView: some View { TabView(selection: .init(get: { diff --git a/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift b/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift index e212fd3b..4e0e5aff 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift @@ -55,9 +55,10 @@ public struct AvatarView: View { .frame(width: size.size.width, height: size.size.height) } else { LazyImage(url: url) { state in - if let image = state.image { - image - .resizingMode(.aspectFit) + if let image = state.imageContainer?.image { + SwiftUI.Image(uiImage: image) + .resizable() + .aspectRatio(contentMode: .fit) } else { placeholderView } diff --git a/Packages/Env/Sources/Env/CustomEnvValues.swift b/Packages/Env/Sources/Env/CustomEnvValues.swift index 22a84dab..d6ada5d6 100644 --- a/Packages/Env/Sources/Env/CustomEnvValues.swift +++ b/Packages/Env/Sources/Env/CustomEnvValues.swift @@ -5,9 +5,27 @@ private struct SecondaryColumnKey: EnvironmentKey { static let defaultValue = false } +private struct ExtraLeadingInset: EnvironmentKey { + static let defaultValue: CGFloat = 0 +} + +private struct IsCompact: EnvironmentKey { + static let defaultValue: Bool = false +} + public extension EnvironmentValues { var isSecondaryColumn: Bool { get { self[SecondaryColumnKey.self] } set { self[SecondaryColumnKey.self] = newValue } } + + var extraLeadingInset: CGFloat { + get { self[ExtraLeadingInset.self] } + set { self[ExtraLeadingInset.self] = newValue } + } + + var isCompact: Bool { + get { self[IsCompact.self] } + set { self[IsCompact.self] = newValue } + } } diff --git a/Packages/Explore/Sources/Explore/ExploreView.swift b/Packages/Explore/Sources/Explore/ExploreView.swift index 1a5a3e55..222c4984 100644 --- a/Packages/Explore/Sources/Explore/ExploreView.swift +++ b/Packages/Explore/Sources/Explore/ExploreView.swift @@ -81,7 +81,7 @@ public struct ExploreView: View { private var loadingView: some View { ForEach(Status.placeholders()) { status in - StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath, isCompact: false)) + StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath)) .padding(.vertical, 8) .redacted(reason: .placeholder) .listRowBackground(theme.primaryBackgroundColor) @@ -184,7 +184,7 @@ public struct ExploreView: View { Section("explore.section.trending.posts") { ForEach(viewModel.trendingStatuses .prefix(upTo: viewModel.trendingStatuses.count > 3 ? 3 : viewModel.trendingStatuses.count)) { status in - StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath, isCompact: false)) + StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath)) .listRowBackground(theme.primaryBackgroundColor) .padding(.vertical, 8) } @@ -192,7 +192,7 @@ public struct ExploreView: View { NavigationLink { List { ForEach(viewModel.trendingStatuses) { status in - StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath, isCompact: false)) + StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath)) .listRowBackground(theme.primaryBackgroundColor) .padding(.vertical, 8) } diff --git a/Packages/Notifications/Sources/Notifications/Notification+Consolidated.swift b/Packages/Notifications/Sources/Notifications/Notification+Consolidated.swift index 95437ee4..c0208e42 100644 --- a/Packages/Notifications/Sources/Notifications/Notification+Consolidated.swift +++ b/Packages/Notifications/Sources/Notifications/Notification+Consolidated.swift @@ -6,24 +6,31 @@ // import Models +import Foundation -extension Array where Element == Notification { - func consolidated(selectedType: Notification.NotificationType?) -> [ConsolidatedNotification] { - Dictionary(grouping: self) { $0.consolidationId(selectedType: selectedType) } - .values - .compactMap { notifications in - guard let notification = notifications.first, - let supportedType = notification.supportedType - else { return nil } +extension Array where Element == Models.Notification { + func consolidated(selectedType: Models.Notification.NotificationType?) async -> [ConsolidatedNotification] { + await withCheckedContinuation({ result in + DispatchQueue.global().async { + let notifications: [ConsolidatedNotification] = + Dictionary(grouping: self) { $0.consolidationId(selectedType: selectedType) } + .values + .compactMap { notifications in + guard let notification = notifications.first, + let supportedType = notification.supportedType + else { return nil } - return ConsolidatedNotification(notifications: notifications, - type: supportedType, - createdAt: notification.createdAt, - accounts: notifications.map(\.account), - status: notification.status) - } - .sorted { - $0.createdAt.asDate > $1.createdAt.asDate + return ConsolidatedNotification(notifications: notifications, + type: supportedType, + createdAt: notification.createdAt, + accounts: notifications.map(\.account), + status: notification.status) + } + .sorted { + $0.createdAt.asDate > $1.createdAt.asDate + } + result.resume(returning: notifications) } + }) } } diff --git a/Packages/Notifications/Sources/Notifications/NotificationRowView.swift b/Packages/Notifications/Sources/Notifications/NotificationRowView.swift index e5216f88..2fba65f6 100644 --- a/Packages/Notifications/Sources/Notifications/NotificationRowView.swift +++ b/Packages/Notifications/Sources/Notifications/NotificationRowView.swift @@ -7,13 +7,13 @@ import SwiftUI import Network struct NotificationRowView: View { - @EnvironmentObject private var currentAccount: CurrentAccount @EnvironmentObject private var theme: Theme @Environment(\.redactionReasons) private var reasons let notification: ConsolidatedNotification let client: Client let routerPath: RouterPath + let followRequests: [Account] var body: some View { HStack(alignment: .top, spacing: 8) { @@ -28,8 +28,7 @@ struct NotificationRowView: View { makeMainLabel(type: notification.type) makeContent(type: notification.type) if notification.type == .follow_request, - currentAccount.followRequests.map(\.id).contains(notification.accounts[0].id) - { + followRequests.map(\.id).contains(notification.accounts[0].id) { FollowRequestButtons(account: notification.accounts[0]) } } @@ -137,19 +136,18 @@ struct NotificationRowView: View { StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath, - isCompact: true, showActions: true)) } else { StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath, - isCompact: true, showActions: false)) .lineLimit(4) .foregroundColor(.gray) } Spacer() } + .environment(\.isCompact, true) } else { Group { Text("@\(notification.accounts[0].acct)") diff --git a/Packages/Notifications/Sources/Notifications/NotificationsListView.swift b/Packages/Notifications/Sources/Notifications/NotificationsListView.swift index cc1b023e..6fc25403 100644 --- a/Packages/Notifications/Sources/Notifications/NotificationsListView.swift +++ b/Packages/Notifications/Sources/Notifications/NotificationsListView.swift @@ -84,7 +84,10 @@ public struct NotificationsListView: View { switch viewModel.state { case .loading: ForEach(ConsolidatedNotification.placeholders()) { notification in - NotificationRowView(notification: notification, client: client, routerPath: routerPath) + NotificationRowView(notification: notification, + client: client, + routerPath: routerPath, + followRequests: account.followRequests) .redacted(reason: .placeholder) .listRowInsets(.init(top: 12, leading: .layoutPadding + 4, @@ -103,13 +106,17 @@ public struct NotificationsListView: View { .listSectionSeparator(.hidden) } else { ForEach(notifications) { notification in - NotificationRowView(notification: notification, client: client, routerPath: routerPath) + NotificationRowView(notification: notification, + client: client, + routerPath: routerPath, + followRequests: account.followRequests) .listRowInsets(.init(top: 12, leading: .layoutPadding + 4, bottom: 12, trailing: .layoutPadding)) .listRowBackground(notification.type == .mention && lockedType != .mention ? theme.secondaryBackgroundColor : theme.primaryBackgroundColor) + .id(notification.id) } } diff --git a/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift b/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift index 70653901..aea80cd0 100644 --- a/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift +++ b/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift @@ -69,7 +69,7 @@ class NotificationsViewModel: ObservableObject { maxId: nil, types: queryTypes, limit: Constants.notificationLimit)) - consolidatedNotifications = notifications.consolidated(selectedType: selectedType) + consolidatedNotifications = await notifications.consolidated(selectedType: selectedType) nextPageState = notifications.count < Constants.notificationLimit ? .none : .hasNextPage } else if let firstId = consolidatedNotifications.first?.id { var newNotifications: [Models.Notification] = await fetchNewPages(minId: firstId, maxPages: 10) @@ -77,13 +77,15 @@ class NotificationsViewModel: ObservableObject { newNotifications = newNotifications.filter { notification in !consolidatedNotifications.contains(where: { $0.id == notification.id }) } - consolidatedNotifications.insert( + await consolidatedNotifications.insert( contentsOf: newNotifications.consolidated(selectedType: selectedType), at: 0 ) } - await currentAccount.fetchFollowerRequests() + if consolidatedNotifications.contains(where: { $0.type == .follow_request }) { + await currentAccount.fetchFollowerRequests() + } withAnimation { state = .display(notifications: consolidatedNotifications, @@ -129,8 +131,10 @@ class NotificationsViewModel: ObservableObject { maxId: lastId, types: queryTypes, limit: Constants.notificationLimit)) - consolidatedNotifications.append(contentsOf: newNotifications.consolidated(selectedType: selectedType)) - await currentAccount?.fetchFollowerRequests() + await consolidatedNotifications.append(contentsOf: newNotifications.consolidated(selectedType: selectedType)) + if consolidatedNotifications.contains(where: { $0.type == .follow_request }) { + await currentAccount?.fetchFollowerRequests() + } state = .display(notifications: consolidatedNotifications, nextPageState: newNotifications.count < Constants.notificationLimit ? .none : .hasNextPage) } catch { @@ -159,14 +163,14 @@ class NotificationsViewModel: ObservableObject { { // If the notification type can be consolidated, try to consolidate with the latest row let latestConsolidatedNotification = consolidatedNotifications.removeFirst() - consolidatedNotifications.insert( + await consolidatedNotifications.insert( contentsOf: ([event.notification] + latestConsolidatedNotification.notifications) .consolidated(selectedType: selectedType), at: 0 ) } else { // Otherwise, just insert the new notification - consolidatedNotifications.insert( + await consolidatedNotifications.insert( contentsOf: [event.notification].consolidated(selectedType: selectedType), at: 0 ) diff --git a/Packages/Status/Sources/Status/Detail/StatusDetailView.swift b/Packages/Status/Sources/Status/Detail/StatusDetailView.swift index 4a5daa64..a0c30be4 100644 --- a/Packages/Status/Sources/Status/Detail/StatusDetailView.swift +++ b/Packages/Status/Sources/Status/Detail/StatusDetailView.swift @@ -104,18 +104,20 @@ public struct StatusDetailView: View { } let viewModel: StatusRowViewModel = .init(status: status, client: client, - routerPath: routerPath, - isCompact: false) - return HStack { + routerPath: routerPath) + return HStack(spacing: 0) { if isReplyToPrevious { Rectangle() .fill(theme.tintColor) .frame(width: 2) + Spacer(minLength: 8) } if self.viewModel.statusId == status.id { makeCurrentStatusView(status: status) + .environment(\.extraLeadingInset, isReplyToPrevious ? 10 : 0) } else { StatusRowView(viewModel: viewModel) + .environment(\.extraLeadingInset, isReplyToPrevious ? 10 : 0) } } .listRowBackground(viewModel.highlightRowColor) @@ -130,7 +132,6 @@ public struct StatusDetailView: View { StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath, - isCompact: false, isFocused: !viewModel.isLoadingContext)) .overlay { GeometryReader { reader in @@ -157,7 +158,7 @@ public struct StatusDetailView: View { private var loadingDetailView: some View { ForEach(Status.placeholders()) { status in - StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath, isCompact: false)) + StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath)) .redacted(reason: .placeholder) } } diff --git a/Packages/Status/Sources/Status/Editor/Components/StatusEditorMediaView.swift b/Packages/Status/Sources/Status/Editor/Components/StatusEditorMediaView.swift index 8f9df62f..fc1f82ee 100644 --- a/Packages/Status/Sources/Status/Editor/Components/StatusEditorMediaView.swift +++ b/Packages/Status/Sources/Status/Editor/Components/StatusEditorMediaView.swift @@ -5,7 +5,7 @@ import Models import NukeUI import SwiftUI -struct StatusEditorMediaView: View { +struct StatusEditorMediaView: View { @EnvironmentObject private var theme: Theme @EnvironmentObject private var currentInstance: CurrentInstance @ObservedObject var viewModel: StatusEditorViewModel diff --git a/Packages/Status/Sources/Status/Embed/StatusEmbededView.swift b/Packages/Status/Sources/Status/Embed/StatusEmbededView.swift index f1ce2fcc..4dfe08b3 100644 --- a/Packages/Status/Sources/Status/Embed/StatusEmbededView.swift +++ b/Packages/Status/Sources/Status/Embed/StatusEmbededView.swift @@ -26,8 +26,8 @@ public struct StatusEmbeddedView: View { StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath, - isCompact: true, showActions: false)) + .environment(\.isCompact, true) } Spacer() } diff --git a/Packages/Status/Sources/Status/List/StatusesListView.swift b/Packages/Status/Sources/Status/List/StatusesListView.swift index 0a1fc8af..dc45f9f3 100644 --- a/Packages/Status/Sources/Status/List/StatusesListView.swift +++ b/Packages/Status/Sources/Status/List/StatusesListView.swift @@ -24,7 +24,7 @@ public struct StatusesListView: View where Fetcher: StatusesFetcher { switch fetcher.statusesState { case .loading: ForEach(Status.placeholders()) { status in - StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath, isCompact: false)) + StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath)) .redacted(reason: .placeholder) } case .error: @@ -40,7 +40,7 @@ public struct StatusesListView: View where Fetcher: StatusesFetcher { case let .display(statuses, nextPageState): ForEach(statuses, id: \.viewId) { status in - let viewModel = StatusRowViewModel(status: status, client: client, routerPath: routerPath, isCompact: false, isRemote: isRemote) + let viewModel = StatusRowViewModel(status: status, client: client, routerPath: routerPath, isRemote: isRemote) if viewModel.filter?.filter.filterAction != .hide { StatusRowView(viewModel: viewModel) .id(status.id) diff --git a/Packages/Status/Sources/Status/Media/VideoPlayerView.swift b/Packages/Status/Sources/Status/Media/VideoPlayerView.swift index 571ecda4..410c2267 100644 --- a/Packages/Status/Sources/Status/Media/VideoPlayerView.swift +++ b/Packages/Status/Sources/Status/Media/VideoPlayerView.swift @@ -54,6 +54,7 @@ struct VideoPlayerView: View { }.onAppear { viewModel.preparePlayer(autoPlay: preferences.autoPlayVideo) } + .cornerRadius(4) .onChange(of: scenePhase, perform: { scenePhase in switch scenePhase { case .background, .inactive: diff --git a/Packages/Status/Sources/Status/Row/StatusRowView.swift b/Packages/Status/Sources/Status/Row/StatusRowView.swift index e65ec9eb..706a45e6 100644 --- a/Packages/Status/Sources/Status/Row/StatusRowView.swift +++ b/Packages/Status/Sources/Status/Row/StatusRowView.swift @@ -8,6 +8,7 @@ import SwiftUI public struct StatusRowView: View { @Environment(\.redactionReasons) private var reasons + @Environment(\.isCompact) private var isCompact: Bool @EnvironmentObject private var theme: Theme @@ -34,12 +35,12 @@ public struct StatusRowView: View { } else { let status: AnyStatus = viewModel.status.reblog ?? viewModel.status VStack(alignment: .leading) { - if !viewModel.isCompact, theme.avatarPosition == .leading { + if !isCompact, theme.avatarPosition == .leading { StatusRowReblogView(viewModel: viewModel) StatusRowReplyView(viewModel: viewModel) } HStack(alignment: .top, spacing: .statusColumnsSpacing) { - if !viewModel.isCompact, + if !isCompact, theme.avatarPosition == .leading { Button { viewModel.routerPath.navigate(to: .accountDetailWithAccount(account: status.account)) @@ -48,13 +49,13 @@ public struct StatusRowView: View { } } VStack(alignment: .leading) { - if !viewModel.isCompact, theme.avatarPosition == .top { + if !isCompact, theme.avatarPosition == .top { StatusRowReblogView(viewModel: viewModel) StatusRowReplyView(viewModel: viewModel) } VStack(alignment: .leading, spacing: 8) { let status: AnyStatus = viewModel.status.reblog ?? viewModel.status - if !viewModel.isCompact { + if !isCompact { StatusRowHeaderView(status: status, viewModel: viewModel) } StatusRowContentView(status: status, viewModel: viewModel) @@ -82,7 +83,7 @@ public struct StatusRowView: View { .onAppear { viewModel.markSeen() if reasons.isEmpty { - if !viewModel.isCompact, viewModel.embeddedStatus == nil { + if !isCompact, viewModel.embeddedStatus == nil { Task { await viewModel.loadEmbeddedStatus() } @@ -93,12 +94,12 @@ public struct StatusRowView: View { contextMenu } .swipeActions(edge: .trailing) { - if !viewModel.isCompact { + if !isCompact { StatusRowSwipeView(viewModel: viewModel, mode: .trailing) } } .swipeActions(edge: .leading) { - if !viewModel.isCompact { + if !isCompact { StatusRowSwipeView(viewModel: viewModel, mode: .leading) } } diff --git a/Packages/Status/Sources/Status/Row/StatusRowViewModel.swift b/Packages/Status/Sources/Status/Row/StatusRowViewModel.swift index b940ecef..2e587664 100644 --- a/Packages/Status/Sources/Status/Row/StatusRowViewModel.swift +++ b/Packages/Status/Sources/Status/Row/StatusRowViewModel.swift @@ -9,7 +9,6 @@ import DesignSystem @MainActor public class StatusRowViewModel: ObservableObject { let status: Status - let isCompact: Bool let isFocused: Bool let isRemote: Bool let showActions: Bool @@ -61,7 +60,6 @@ public class StatusRowViewModel: ObservableObject { public init(status: Status, client: Client, routerPath: RouterPath, - isCompact: Bool = false, isFocused: Bool = false, isRemote: Bool = false, showActions: Bool = true) @@ -69,7 +67,6 @@ public class StatusRowViewModel: ObservableObject { self.status = status self.client = client self.routerPath = routerPath - self.isCompact = isCompact self.isFocused = isFocused self.isRemote = isRemote self.showActions = showActions diff --git a/Packages/Status/Sources/Status/Row/Subviews/StatusRowCardView.swift b/Packages/Status/Sources/Status/Row/Subviews/StatusRowCardView.swift index 0deeb430..f98b3320 100644 --- a/Packages/Status/Sources/Status/Row/Subviews/StatusRowCardView.swift +++ b/Packages/Status/Sources/Status/Row/Subviews/StatusRowCardView.swift @@ -18,9 +18,12 @@ public struct StatusRowCardView: View { VStack(alignment: .leading) { if let imageURL = card.image { LazyImage(url: imageURL) { state in - if let image = state.image { - image - .resizingMode(.aspectFill) + if let image = state.imageContainer?.image { + SwiftUI.Image(uiImage: image) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(height: 200) + .clipped() } else if state.isLoading { Rectangle() .fill(Color.gray) diff --git a/Packages/Status/Sources/Status/Row/Subviews/StatusRowContentView.swift b/Packages/Status/Sources/Status/Row/Subviews/StatusRowContentView.swift index 70f36315..88a752a5 100644 --- a/Packages/Status/Sources/Status/Row/Subviews/StatusRowContentView.swift +++ b/Packages/Status/Sources/Status/Row/Subviews/StatusRowContentView.swift @@ -5,6 +5,8 @@ import Env struct StatusRowContentView: View { @Environment(\.redactionReasons) private var reasons + @Environment(\.isCompact) private var isCompact + @EnvironmentObject private var theme: Theme let status: AnyStatus @@ -23,7 +25,7 @@ struct StatusRowContentView: View { } if !reasons.contains(.placeholder), - !viewModel.isCompact, + !isCompact, (viewModel.isEmbedLoading || viewModel.embeddedStatus != nil) { StatusEmbeddedView(status: viewModel.embeddedStatus ?? Status.placeholder(), client: viewModel.client, @@ -31,13 +33,14 @@ struct StatusRowContentView: View { .fixedSize(horizontal: false, vertical: true) .redacted(reason: viewModel.isEmbedLoading ? .placeholder : []) .shimmering(active: viewModel.isEmbedLoading) + .transition(.opacity) } if !status.mediaAttachments.isEmpty { HStack { StatusRowMediaPreviewView(attachments: status.mediaAttachments, sensitive: status.sensitive, - isNotifications: viewModel.isCompact) + isNotifications: isCompact) if theme.statusDisplayStyle == .compact { Spacer() } @@ -47,7 +50,7 @@ struct StatusRowContentView: View { if let card = status.card, !viewModel.isEmbedLoading, - !viewModel.isCompact, + !isCompact, theme.statusDisplayStyle == .large, status.content.statusesURLs.isEmpty, status.mediaAttachments.isEmpty diff --git a/Packages/Status/Sources/Status/Row/Subviews/StatusRowMediaPreviewView.swift b/Packages/Status/Sources/Status/Row/Subviews/StatusRowMediaPreviewView.swift index 74168d73..19487f0d 100644 --- a/Packages/Status/Sources/Status/Row/Subviews/StatusRowMediaPreviewView.swift +++ b/Packages/Status/Sources/Status/Row/Subviews/StatusRowMediaPreviewView.swift @@ -8,6 +8,7 @@ import SwiftUI public struct StatusRowMediaPreviewView: View { @Environment(\.openURL) private var openURL @Environment(\.isSecondaryColumn) private var isSecondaryColumn + @Environment(\.extraLeadingInset) private var extraLeadingInset: CGFloat @EnvironmentObject var sceneDelegate: SceneDelegate @EnvironmentObject private var preferences: UserPreferences @@ -37,7 +38,7 @@ public struct StatusRowMediaPreviewView: View { if UIDevice.current.userInterfaceIdiom == .pad && sceneDelegate.windowWidth < (.maxColumnWidth + .sidebarWidth) { sidebarWidth = .sidebarWidth } - return (.layoutPadding * 2) + avatarColumnWidth + sidebarWidth + return (.layoutPadding * 2) + avatarColumnWidth + sidebarWidth + extraLeadingInset } private var imageMaxHeight: CGFloat { @@ -152,11 +153,13 @@ public struct StatusRowMediaPreviewView: View { switch attachment.supportedType { case .image: LazyImage(url: attachment.url) { state in - if let image = state.image { - image - .resizingMode(.aspectFill) - .cornerRadius(4) + if let image = state.imageContainer?.image { + SwiftUI.Image(uiImage: image) + .resizable() + .aspectRatio(contentMode: .fill) .frame(width: newSize.width, height: newSize.height) + .clipped() + .cornerRadius(4) } else { RoundedRectangle(cornerRadius: 4) .fill(Color.gray) @@ -204,9 +207,13 @@ public struct StatusRowMediaPreviewView: View { case .image: ZStack(alignment: .bottomTrailing) { LazyImage(url: attachment.url) { state in - if let image = state.image { - image - .resizingMode(.aspectFill) + if let image = state.imageContainer?.image { + SwiftUI.Image(uiImage: image) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(maxWidth: isNotifications ? imageMaxHeight : proxy.frame(in: .local).width) + .frame(maxHeight: imageMaxHeight) + .clipped() .cornerRadius(4) } else if state.isLoading { RoundedRectangle(cornerRadius: 4) @@ -215,8 +222,6 @@ public struct StatusRowMediaPreviewView: View { .frame(maxWidth: isNotifications ? imageMaxHeight : proxy.frame(in: .local).width) } } - .frame(maxWidth: isNotifications ? imageMaxHeight : proxy.frame(in: .local).width) - .frame(height: imageMaxHeight) if sensitive { cornerSensitiveButton }