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)
}
}
.processors([ImageProcessors.Resize(size: .init(width: newSize.width, height: newSize.height))])
.frame(width: newSize.width, height: newSize.height)
case .gifv, .video, .audio:
@ -205,13 +206,14 @@ public struct StatusRowMediaPreviewView: View {
GeometryReader { proxy in
switch type {
case .image:
let width = isNotifications ? imageMaxHeight : proxy.frame(in: .local).width
ZStack(alignment: .bottomTrailing) {
LazyImage(url: attachment.url) { state in
LazyImage(url: attachment.previewUrl ?? attachment.url) { state in
if let image = state.imageContainer?.image {
SwiftUI.Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(maxWidth: isNotifications ? imageMaxHeight : proxy.frame(in: .local).width)
.frame(maxWidth: width)
.frame(maxHeight: imageMaxHeight)
.clipped()
.cornerRadius(4)
@ -219,9 +221,10 @@ public struct StatusRowMediaPreviewView: View {
RoundedRectangle(cornerRadius: 4)
.fill(Color.gray)
.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 {
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
@StateObject private var viewModel = TimelineViewModel()
@StateObject private var prefetcher = TimelinePrefetcher()
@State private var wasBackgrounded: Bool = false
@State private var collectionView: UICollectionView?
@ -53,10 +54,12 @@ public struct TimelineView: View {
.listStyle(.plain)
.scrollContentBackground(.hidden)
.background(theme.primaryBackgroundColor)
.introspect(selector: TargetViewSelector.ancestorOrSiblingContaining,
customize: { (collectionView: UICollectionView) in
self.collectionView = collectionView
})
.introspect(selector: TargetViewSelector.ancestorOrSiblingContaining) { (collectionView: UICollectionView) in
self.collectionView = collectionView
self.prefetcher.viewModel = viewModel
collectionView.isPrefetchingEnabled = true
collectionView.prefetchDataSource = self.prefetcher
}
if viewModel.pendingStatusesEnabled {
PendingStatusesObserverView(observer: viewModel.pendingStatusesObserver)
}