Files
yattee/Shared/Videos/ThumbnailView.swift
Arkadiusz Fal 42d53c30db Fix thumbnail aspect ratio in video grid cells
Thumbnails were being stretched vertically due to incorrect aspect ratio handling. Fixed by:
- Using .scaledToFill() on thumbnails to fill the container width
- Constraining container to 16:9 aspect ratio with .fit mode
- Adding matching aspect ratio to placeholder to prevent layout shift during loading

This ensures thumbnails maintain proper proportions while filling the full cell width.
2025-11-19 22:37:05 +01:00

84 lines
2.3 KiB
Swift

import CachedAsyncImage
import SDWebImageSwiftUI
import SwiftUI
struct ThumbnailView: View {
var url: URL?
private let thumbnails = ThumbnailsModel.shared
private let thumbnailExtension: String?
init(url: URL?) {
self.url = url
thumbnailExtension = Self.extractExtension(from: url)
}
private static func extractExtension(from url: URL?) -> String? {
guard let url,
let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return nil }
let pathComponents = urlComponents.path.components(separatedBy: ".")
guard pathComponents.count > 1 else { return nil }
return pathComponents.last
}
var body: some View {
if url != nil {
viewForThumbnailExtension
} else {
placeholder
}
}
@ViewBuilder var viewForThumbnailExtension: some View {
if AccountsModel.shared.app != .piped, thumbnailExtension != nil {
if thumbnailExtension == "webp" {
webImage
} else {
asyncImageIfAvailable
}
} else {
webImage
}
}
var webImage: some View {
WebImage(url: url)
.onFailure { _ in
if let url {
thumbnails.insertUnloadable(url)
}
}
.placeholder { placeholder }
.resizable()
}
@ViewBuilder var asyncImageIfAvailable: some View {
// swiftlint:disable:next deployment_target
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) {
CachedAsyncImage(url: url, urlCache: BaseCacheModel.imageCache) { phase in
switch phase {
case let .success(image):
image
.resizable()
case .failure:
placeholder.onAppear {
guard let url else { return }
thumbnails.insertUnloadable(url)
}
default:
placeholder
}
}
} else {
placeholder
}
}
var placeholder: some View {
Rectangle()
.fill(Color("PlaceholderColor"))
.aspectRatio(Constants.aspectRatio16x9, contentMode: .fit)
}
}