Add image prefetch to the timeline + resize close #931 (#930)

* Add image prefectch to the timeline

* Use preview url for post with multiple attachements

* Add image resize

* Prefetch link cards

---------

Co-authored-by: Thomas Ricouard <ricouard77@gmail.com>
This commit is contained in:
Alex Grebenyuk 2023-02-18 12:44:43 -05:00 committed by GitHub
parent 7cc1ca44b5
commit a8459638e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 7 deletions

View file

@ -166,6 +166,7 @@ public struct StatusRowMediaPreviewView: View {
.frame(width: newSize.width, height: newSize.height) .frame(width: newSize.width, height: newSize.height)
} }
} }
.processors([ImageProcessors.Resize(size: .init(width: newSize.width, height: newSize.height))])
.frame(width: newSize.width, height: newSize.height) .frame(width: newSize.width, height: newSize.height)
case .gifv, .video, .audio: case .gifv, .video, .audio:
@ -205,13 +206,14 @@ public struct StatusRowMediaPreviewView: View {
GeometryReader { proxy in GeometryReader { proxy in
switch type { switch type {
case .image: case .image:
let width = isNotifications ? imageMaxHeight : proxy.frame(in: .local).width
ZStack(alignment: .bottomTrailing) { ZStack(alignment: .bottomTrailing) {
LazyImage(url: attachment.url) { state in LazyImage(url: attachment.previewUrl ?? attachment.url) { state in
if let image = state.imageContainer?.image { if let image = state.imageContainer?.image {
SwiftUI.Image(uiImage: image) SwiftUI.Image(uiImage: image)
.resizable() .resizable()
.aspectRatio(contentMode: .fill) .aspectRatio(contentMode: .fill)
.frame(maxWidth: isNotifications ? imageMaxHeight : proxy.frame(in: .local).width) .frame(maxWidth: width)
.frame(maxHeight: imageMaxHeight) .frame(maxHeight: imageMaxHeight)
.clipped() .clipped()
.cornerRadius(4) .cornerRadius(4)
@ -219,9 +221,10 @@ public struct StatusRowMediaPreviewView: View {
RoundedRectangle(cornerRadius: 4) RoundedRectangle(cornerRadius: 4)
.fill(Color.gray) .fill(Color.gray)
.frame(maxHeight: imageMaxHeight) .frame(maxHeight: imageMaxHeight)
.frame(maxWidth: isNotifications ? imageMaxHeight : proxy.frame(in: .local).width) .frame(maxWidth: width)
} }
} }
.processors([ImageProcessors.Resize(size: .init(width: width, height: imageMaxHeight))])
if sensitive { if sensitive {
cornerSensitiveButton cornerSensitiveButton
} }

View file

@ -0,0 +1,42 @@
import SwiftUI
import UIKit
import Models
import Nuke
final class TimelinePrefetcher: NSObject, ObservableObject, UICollectionViewDataSourcePrefetching {
private let prefetcher = ImagePrefetcher()
weak var viewModel: TimelineViewModel?
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
let imageURLs = getImageURLs(for: indexPaths)
prefetcher.startPrefetching(with: imageURLs)
}
func collectionView(_ collectionView: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath]) {
let imageURLs = getImageURLs(for: indexPaths)
prefetcher.stopPrefetching(with: imageURLs)
}
private func getImageURLs(for indexPaths: [IndexPath]) -> [URL] {
guard let viewModel, case .display(let statuses, _) = viewModel.statusesState else {
return []
}
return indexPaths.compactMap {
$0.row < statuses.endIndex ? statuses[$0.row] : nil
}.flatMap(getImages)
}
}
private func getImages(for status: Status) -> [URL] {
var urls = status.mediaAttachments.compactMap {
if $0.supportedType == .image {
return status.mediaAttachments.count > 1 ? $0.previewUrl ?? $0.url : $0.url
}
return nil
}
if let url = status.card?.image {
urls.append(url)
}
return urls
}

View file

@ -20,6 +20,7 @@ public struct TimelineView: View {
@EnvironmentObject private var routerPath: RouterPath @EnvironmentObject private var routerPath: RouterPath
@StateObject private var viewModel = TimelineViewModel() @StateObject private var viewModel = TimelineViewModel()
@StateObject private var prefetcher = TimelinePrefetcher()
@State private var wasBackgrounded: Bool = false @State private var wasBackgrounded: Bool = false
@State private var collectionView: UICollectionView? @State private var collectionView: UICollectionView?
@ -53,10 +54,12 @@ public struct TimelineView: View {
.listStyle(.plain) .listStyle(.plain)
.scrollContentBackground(.hidden) .scrollContentBackground(.hidden)
.background(theme.primaryBackgroundColor) .background(theme.primaryBackgroundColor)
.introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, .introspect(selector: TargetViewSelector.ancestorOrSiblingContaining) { (collectionView: UICollectionView) in
customize: { (collectionView: UICollectionView) in self.collectionView = collectionView
self.collectionView = collectionView self.prefetcher.viewModel = viewModel
}) collectionView.isPrefetchingEnabled = true
collectionView.prefetchDataSource = self.prefetcher
}
if viewModel.pendingStatusesEnabled { if viewModel.pendingStatusesEnabled {
PendingStatusesObserverView(observer: viewModel.pendingStatusesObserver) PendingStatusesObserverView(observer: viewModel.pendingStatusesObserver)
} }