2023-01-03 14:09:22 +01:00
|
|
|
//
|
|
|
|
// https://mczachurski.dev
|
|
|
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
2023-03-28 10:35:38 +02:00
|
|
|
// Licensed under the Apache License 2.0.
|
2023-01-03 14:09:22 +01:00
|
|
|
//
|
2023-04-01 12:10:59 +02:00
|
|
|
|
2023-01-03 14:09:22 +01:00
|
|
|
import SwiftUI
|
2023-02-19 10:32:38 +01:00
|
|
|
import PixelfedKit
|
2023-04-07 14:20:12 +02:00
|
|
|
import ClientKit
|
2023-04-07 14:38:50 +02:00
|
|
|
import ServicesKit
|
2023-01-03 14:09:22 +01:00
|
|
|
|
|
|
|
struct ImagesCarousel: View {
|
2023-01-31 12:20:49 +01:00
|
|
|
@State public var attachments: [AttachmentModel]
|
2023-01-26 15:10:47 +01:00
|
|
|
@State private var imageHeight: Double
|
|
|
|
@State private var imageWidth: Double
|
|
|
|
@State private var selected: String
|
|
|
|
@State private var heightWasPrecalculated: Bool
|
2023-04-01 12:10:59 +02:00
|
|
|
|
2023-03-08 18:21:37 +01:00
|
|
|
@Binding public var selectedAttachment: AttachmentModel?
|
2023-01-08 15:43:55 +01:00
|
|
|
@Binding public var exifCamera: String?
|
|
|
|
@Binding public var exifExposure: String?
|
|
|
|
@Binding public var exifCreatedDate: String?
|
|
|
|
@Binding public var exifLens: String?
|
2023-03-08 17:43:05 +01:00
|
|
|
@Binding public var description: String?
|
2023-04-01 12:10:59 +02:00
|
|
|
|
2023-01-31 12:20:49 +01:00
|
|
|
init(attachments: [AttachmentModel],
|
2023-03-08 18:21:37 +01:00
|
|
|
selectedAttachment: Binding<AttachmentModel?>,
|
2023-01-26 15:10:47 +01:00
|
|
|
exifCamera: Binding<String?>,
|
|
|
|
exifExposure: Binding<String?>,
|
|
|
|
exifCreatedDate: Binding<String?>,
|
2023-03-08 17:43:05 +01:00
|
|
|
exifLens: Binding<String?>,
|
|
|
|
description: Binding<String?>
|
2023-01-26 15:10:47 +01:00
|
|
|
) {
|
2023-03-08 18:21:37 +01:00
|
|
|
_selectedAttachment = selectedAttachment
|
2023-01-26 15:10:47 +01:00
|
|
|
_exifCamera = exifCamera
|
|
|
|
_exifExposure = exifExposure
|
|
|
|
_exifCreatedDate = exifCreatedDate
|
|
|
|
_exifLens = exifLens
|
2023-03-08 17:43:05 +01:00
|
|
|
_description = description
|
2023-04-01 12:10:59 +02:00
|
|
|
|
2023-01-26 15:10:47 +01:00
|
|
|
self.attachments = attachments
|
|
|
|
self.selected = String.empty()
|
|
|
|
|
2023-01-27 15:46:09 +01:00
|
|
|
let highestImage = attachments.getHighestImage()
|
|
|
|
let imgHeight = Double((highestImage?.meta as? ImageMetadata)?.original?.height ?? 0)
|
|
|
|
let imgWidth = Double((highestImage?.meta as? ImageMetadata)?.original?.width ?? 0)
|
2023-04-01 12:10:59 +02:00
|
|
|
|
2023-01-26 20:35:24 +01:00
|
|
|
// Calculate size of frame (first from cache, then from metadata).
|
2023-01-28 21:09:31 +01:00
|
|
|
if let highestImage, let size = ImageSizeService.shared.get(for: highestImage.url) {
|
2023-09-16 10:39:21 +02:00
|
|
|
let calculatedSize = ImageSizeService.shared.calculate(width: size.width, height: size.height)
|
2023-09-10 12:03:08 +02:00
|
|
|
|
|
|
|
self.imageWidth = calculatedSize.width
|
|
|
|
self.imageHeight = calculatedSize.height
|
2023-01-26 20:35:24 +01:00
|
|
|
|
|
|
|
self.heightWasPrecalculated = true
|
2023-01-27 15:46:09 +01:00
|
|
|
} else if let highestImage, imgHeight > 0 && imgWidth > 0 {
|
2023-05-25 17:33:04 +02:00
|
|
|
ImageSizeService.shared.save(for: highestImage.url, width: imgWidth, height: imgHeight)
|
2023-09-16 10:39:21 +02:00
|
|
|
let size = ImageSizeService.shared.calculate(for: highestImage.url)
|
|
|
|
|
2023-01-26 20:35:24 +01:00
|
|
|
self.imageWidth = size.width
|
|
|
|
self.imageHeight = size.height
|
|
|
|
|
2023-01-26 15:10:47 +01:00
|
|
|
self.heightWasPrecalculated = true
|
|
|
|
} else {
|
|
|
|
self.imageWidth = UIScreen.main.bounds.width
|
|
|
|
self.imageHeight = UIScreen.main.bounds.width * 0.75
|
|
|
|
self.heightWasPrecalculated = false
|
|
|
|
}
|
|
|
|
}
|
2023-04-01 12:10:59 +02:00
|
|
|
|
2023-01-03 14:09:22 +01:00
|
|
|
var body: some View {
|
2023-09-17 08:27:45 +02:00
|
|
|
HStack(spacing: 0) {
|
|
|
|
Spacer(minLength: 0)
|
2023-09-16 10:39:21 +02:00
|
|
|
TabView(selection: $selected) {
|
|
|
|
ForEach(attachments, id: \.id) { attachment in
|
|
|
|
ImageCarouselPicture(attachment: attachment) { (attachment, imageData) in
|
|
|
|
withAnimation {
|
|
|
|
self.recalculateImageHeight(attachment: attachment, imageData: imageData)
|
|
|
|
}
|
|
|
|
|
|
|
|
self.asyncAfter(0.4) {
|
|
|
|
attachment.set(data: imageData)
|
|
|
|
}
|
2023-04-01 12:10:59 +02:00
|
|
|
|
2023-01-26 15:10:47 +01:00
|
|
|
}
|
2023-09-16 10:39:21 +02:00
|
|
|
.tag(attachment.id)
|
2023-01-03 14:09:22 +01:00
|
|
|
}
|
|
|
|
}
|
2023-09-17 08:27:45 +02:00
|
|
|
.padding(0)
|
2023-09-16 10:39:21 +02:00
|
|
|
.frame(height: self.imageHeight)
|
|
|
|
.tabViewStyle(PageTabViewStyle())
|
2023-10-19 09:29:49 +02:00
|
|
|
.onChange(of: selected) { oldIndex, newIndex in
|
|
|
|
if let attachment = attachments.first(where: { item in item.id == newIndex }) {
|
2023-09-16 10:39:21 +02:00
|
|
|
self.selectedAttachment = attachment
|
|
|
|
self.exifCamera = attachment.exifCamera
|
|
|
|
self.exifExposure = attachment.exifExposure
|
|
|
|
self.exifCreatedDate = attachment.exifCreatedDate
|
|
|
|
self.exifLens = attachment.exifLens
|
|
|
|
self.description = attachment.description
|
|
|
|
}
|
2023-10-19 09:29:49 +02:00
|
|
|
}
|
2023-09-17 08:27:45 +02:00
|
|
|
Spacer(minLength: 0)
|
2023-01-03 14:09:22 +01:00
|
|
|
}
|
2023-09-17 08:27:45 +02:00
|
|
|
.padding(0)
|
2023-01-03 14:09:22 +01:00
|
|
|
.onAppear {
|
2023-01-14 11:57:28 +01:00
|
|
|
self.selected = self.attachments.first?.id ?? String.empty()
|
2023-01-03 14:09:22 +01:00
|
|
|
}
|
|
|
|
}
|
2023-04-01 12:10:59 +02:00
|
|
|
|
2023-01-31 12:20:49 +01:00
|
|
|
private func recalculateImageHeight(attachment: AttachmentModel, imageData: Data) {
|
2023-01-26 15:10:47 +01:00
|
|
|
guard heightWasPrecalculated == false else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-09-16 10:39:21 +02:00
|
|
|
var maxImageHeight = 0.0
|
|
|
|
var maxImageWidth = 0.0
|
2023-04-01 12:10:59 +02:00
|
|
|
|
2023-01-03 14:09:22 +01:00
|
|
|
for item in attachments {
|
2023-09-16 10:39:21 +02:00
|
|
|
// Get attachment sizes from cache.
|
|
|
|
if let attachmentSize = ImageSizeService.shared.get(for: item.url) {
|
|
|
|
if attachmentSize.height > maxImageHeight {
|
|
|
|
maxImageHeight = attachmentSize.height
|
|
|
|
maxImageWidth = attachmentSize.width
|
|
|
|
}
|
|
|
|
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// When we don't have in cache read from data and add to cache.
|
2023-01-10 20:38:02 +01:00
|
|
|
if let data = item.data, let image = UIImage(data: data) {
|
2023-09-16 10:39:21 +02:00
|
|
|
ImageSizeService.shared.save(for: item.url, width: image.size.width, height: image.size.height)
|
2023-05-25 17:33:04 +02:00
|
|
|
|
2023-09-16 10:39:21 +02:00
|
|
|
if image.size.height > maxImageHeight {
|
|
|
|
maxImageHeight = image.size.height
|
|
|
|
maxImageWidth = image.size.width
|
2023-01-03 14:09:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-04-01 12:10:59 +02:00
|
|
|
|
2023-01-26 15:10:47 +01:00
|
|
|
if let image = UIImage(data: imageData) {
|
2023-09-16 10:39:21 +02:00
|
|
|
ImageSizeService.shared.save(for: attachment.url, width: image.size.width, height: image.size.height)
|
|
|
|
|
|
|
|
if image.size.height > maxImageHeight {
|
|
|
|
maxImageHeight = image.size.height
|
|
|
|
maxImageWidth = image.size.width
|
2023-01-26 15:10:47 +01:00
|
|
|
}
|
|
|
|
}
|
2023-04-01 12:10:59 +02:00
|
|
|
|
2023-09-16 10:39:21 +02:00
|
|
|
let size = ImageSizeService.shared.calculate(width: maxImageWidth, height: maxImageHeight)
|
2023-01-26 20:35:24 +01:00
|
|
|
self.imageWidth = size.width
|
|
|
|
self.imageHeight = size.height
|
2023-01-03 14:09:22 +01:00
|
|
|
}
|
|
|
|
}
|