Add blurhash support for main status image.

This commit is contained in:
Marcin Czachursk 2023-01-09 13:05:02 +01:00
parent 8a5552b21a
commit 68351897b0
8 changed files with 89 additions and 13 deletions

View File

@ -53,6 +53,7 @@
F86B7214296BFDCE00EE59EC /* UserProfileHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F86B7213296BFDCE00EE59EC /* UserProfileHeader.swift */; }; F86B7214296BFDCE00EE59EC /* UserProfileHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F86B7213296BFDCE00EE59EC /* UserProfileHeader.swift */; };
F86B7216296BFFDA00EE59EC /* UserProfileStatuses.swift in Sources */ = {isa = PBXBuildFile; fileRef = F86B7215296BFFDA00EE59EC /* UserProfileStatuses.swift */; }; F86B7216296BFFDA00EE59EC /* UserProfileStatuses.swift in Sources */ = {isa = PBXBuildFile; fileRef = F86B7215296BFFDA00EE59EC /* UserProfileStatuses.swift */; };
F86B7218296C27C100EE59EC /* ActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F86B7217296C27C100EE59EC /* ActionButton.swift */; }; F86B7218296C27C100EE59EC /* ActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F86B7217296C27C100EE59EC /* ActionButton.swift */; };
F86B721C296C394000EE59EC /* Status+ImageSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F86B721B296C394000EE59EC /* Status+ImageSize.swift */; };
F88ABD9229686F1C004EF61E /* MemoryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88ABD9129686F1C004EF61E /* MemoryCache.swift */; }; F88ABD9229686F1C004EF61E /* MemoryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88ABD9129686F1C004EF61E /* MemoryCache.swift */; };
F88ABD9429687CA4004EF61E /* ComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88ABD9329687CA4004EF61E /* ComposeView.swift */; }; F88ABD9429687CA4004EF61E /* ComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88ABD9329687CA4004EF61E /* ComposeView.swift */; };
F88C246C295C37B80006098B /* VernissageApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C246B295C37B80006098B /* VernissageApp.swift */; }; F88C246C295C37B80006098B /* VernissageApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C246B295C37B80006098B /* VernissageApp.swift */; };
@ -129,6 +130,7 @@
F86B7213296BFDCE00EE59EC /* UserProfileHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileHeader.swift; sourceTree = "<group>"; }; F86B7213296BFDCE00EE59EC /* UserProfileHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileHeader.swift; sourceTree = "<group>"; };
F86B7215296BFFDA00EE59EC /* UserProfileStatuses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileStatuses.swift; sourceTree = "<group>"; }; F86B7215296BFFDA00EE59EC /* UserProfileStatuses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileStatuses.swift; sourceTree = "<group>"; };
F86B7217296C27C100EE59EC /* ActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButton.swift; sourceTree = "<group>"; }; F86B7217296C27C100EE59EC /* ActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButton.swift; sourceTree = "<group>"; };
F86B721B296C394000EE59EC /* Status+ImageSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Status+ImageSize.swift"; sourceTree = "<group>"; };
F88ABD9129686F1C004EF61E /* MemoryCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryCache.swift; sourceTree = "<group>"; }; F88ABD9129686F1C004EF61E /* MemoryCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryCache.swift; sourceTree = "<group>"; };
F88ABD9329687CA4004EF61E /* ComposeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeView.swift; sourceTree = "<group>"; }; F88ABD9329687CA4004EF61E /* ComposeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeView.swift; sourceTree = "<group>"; };
F88ABD9529687D4D004EF61E /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; }; F88ABD9529687D4D004EF61E /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
@ -218,6 +220,7 @@
F8210DE62966E1D1001D9973 /* Color+Assets.swift */, F8210DE62966E1D1001D9973 /* Color+Assets.swift */,
F8C14393296AF21B001FE31D /* Double+Round.swift */, F8C14393296AF21B001FE31D /* Double+Round.swift */,
F8984E4C296B648000A2610F /* UIImage+Blurhash.swift */, F8984E4C296B648000A2610F /* UIImage+Blurhash.swift */,
F86B721B296C394000EE59EC /* Status+ImageSize.swift */,
); );
path = Extensions; path = Extensions;
sourceTree = "<group>"; sourceTree = "<group>";
@ -456,6 +459,7 @@
F80048082961E6DE00E6868A /* StatusDataHandler.swift in Sources */, F80048082961E6DE00E6868A /* StatusDataHandler.swift in Sources */,
F866F6A0296040A8002E8F88 /* ApplicationSettings+CoreDataClass.swift in Sources */, F866F6A0296040A8002E8F88 /* ApplicationSettings+CoreDataClass.swift in Sources */,
F8210DEA2966E4F9001D9973 /* AnimatePlaceholderModifier.swift in Sources */, F8210DEA2966E4F9001D9973 /* AnimatePlaceholderModifier.swift in Sources */,
F86B721C296C394000EE59EC /* Status+ImageSize.swift in Sources */,
F8984E4D296B648000A2610F /* UIImage+Blurhash.swift in Sources */, F8984E4D296B648000A2610F /* UIImage+Blurhash.swift in Sources */,
F897978A2968314A00B22335 /* LoadingIndicator.swift in Sources */, F897978A2968314A00B22335 /* LoadingIndicator.swift in Sources */,
F8210DE52966E160001D9973 /* Color+SystemColors.swift in Sources */, F8210DE52966E160001D9973 /* Color+SystemColors.swift in Sources */,

View File

@ -17,5 +17,13 @@ extension AttachmentData {
self.remoteUrl = attachment.remoteUrl self.remoteUrl = attachment.remoteUrl
self.text = attachment.description self.text = attachment.description
self.type = attachment.type.rawValue self.type = attachment.type.rawValue
if let width = (attachment.meta as? ImageMetadata)?.original?.width {
self.metaImageWidth = Int32(width)
}
if let height = (attachment.meta as? ImageMetadata)?.original?.height {
self.metaImageHeight = Int32(height)
}
} }
} }

View File

@ -4,9 +4,12 @@
// Licensed under the MIT License. // Licensed under the MIT License.
// //
//
import Foundation import Foundation
import CoreData import CoreData
extension AttachmentData { extension AttachmentData {
@nonobjc public class func fetchRequest() -> NSFetchRequest<AttachmentData> { @nonobjc public class func fetchRequest() -> NSFetchRequest<AttachmentData> {
@ -15,6 +18,10 @@ extension AttachmentData {
@NSManaged public var blurhash: String? @NSManaged public var blurhash: String?
@NSManaged public var data: Data @NSManaged public var data: Data
@NSManaged public var exifCamera: String?
@NSManaged public var exifCreatedDate: String?
@NSManaged public var exifExposure: String?
@NSManaged public var exifLens: String?
@NSManaged public var id: String @NSManaged public var id: String
@NSManaged public var previewUrl: URL? @NSManaged public var previewUrl: URL?
@NSManaged public var remoteUrl: URL? @NSManaged public var remoteUrl: URL?
@ -22,13 +29,12 @@ extension AttachmentData {
@NSManaged public var text: String? @NSManaged public var text: String?
@NSManaged public var type: String @NSManaged public var type: String
@NSManaged public var url: URL @NSManaged public var url: URL
@NSManaged public var exifCamera: String? @NSManaged public var metaImageWidth: Int32
@NSManaged public var exifLens: String? @NSManaged public var metaImageHeight: Int32
@NSManaged public var exifExposure: String?
@NSManaged public var exifCreatedDate: String?
@NSManaged public var statusRelation: StatusData? @NSManaged public var statusRelation: StatusData?
} }
extension AttachmentData : Identifiable { extension AttachmentData : Identifiable {
} }

View File

@ -0,0 +1,26 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import MastodonSwift
extension Status {
public func getImageWidth() -> Int32? {
if let width = (self.mediaAttachments.first?.meta as? ImageMetadata)?.original?.width {
return Int32(width)
} else {
return nil
}
}
public func getImageHeight() -> Int32? {
if let height = (self.mediaAttachments.first?.meta as? ImageMetadata)?.original?.height {
return Int32(height)
} else {
return nil
}
}
}

View File

@ -32,6 +32,8 @@
<attribute name="exifExposure" optional="YES" attributeType="String"/> <attribute name="exifExposure" optional="YES" attributeType="String"/>
<attribute name="exifLens" optional="YES" attributeType="String"/> <attribute name="exifLens" optional="YES" attributeType="String"/>
<attribute name="id" attributeType="String"/> <attribute name="id" attributeType="String"/>
<attribute name="metaImageHeight" optional="YES" attributeType="Integer 32" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="metaImageWidth" optional="YES" attributeType="Integer 32" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="previewUrl" optional="YES" attributeType="URI"/> <attribute name="previewUrl" optional="YES" attributeType="URI"/>
<attribute name="remoteUrl" optional="YES" attributeType="URI"/> <attribute name="remoteUrl" optional="YES" attributeType="URI"/>
<attribute name="statusId" attributeType="String"/> <attribute name="statusId" attributeType="String"/>

View File

@ -22,7 +22,10 @@ struct HomeFeedView: View {
ScrollView { ScrollView {
LazyVGrid(columns: gridColumns) { LazyVGrid(columns: gridColumns) {
ForEach(dbStatuses, id: \.self) { item in ForEach(dbStatuses, id: \.self) { item in
NavigationLink(destination: StatusView(statusId: item.id) NavigationLink(destination: StatusView(statusId: item.id,
imageBlurhash: item.attachments().first?.blurhash,
imageWidth: item.attachments().first?.metaImageWidth,
imageHeight: item.attachments().first?.metaImageHeight)
.environmentObject(applicationState)) { .environmentObject(applicationState)) {
ImageRow(attachments: item.attachments()) ImageRow(attachments: item.attachments())
} }

View File

@ -11,6 +11,9 @@ import AVFoundation
struct StatusView: View { struct StatusView: View {
@EnvironmentObject var applicationState: ApplicationState @EnvironmentObject var applicationState: ApplicationState
@State var statusId: String @State var statusId: String
@State var imageBlurhash: String?
@State var imageWidth: Int32?
@State var imageHeight: Int32?
@State private var showCompose = false @State private var showCompose = false
@State private var statusData: StatusData? @State private var statusData: StatusData?
@ -84,9 +87,16 @@ struct StatusView: View {
} }
} else { } else {
VStack (alignment: .leading) { VStack (alignment: .leading) {
if let imageBlurhash, let uiImage = UIImage(blurHash: imageBlurhash, size: CGSize(width: 32, height: 32)) {
Image(uiImage: uiImage)
.resizable()
.frame(width: UIScreen.main.bounds.width, height: self.getImageHeight())
} else {
Rectangle() Rectangle()
.fill(Color.placeholderText) .fill(Color.placeholderText)
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.width) .frame(width: UIScreen.main.bounds.width, height: self.getImageHeight())
.redacted(reason: .placeholder)
}
VStack(alignment: .leading) { VStack(alignment: .leading) {
UsernameRow(accountDisplayName: "Verylong Displayname", UsernameRow(accountDisplayName: "Verylong Displayname",
@ -103,12 +113,13 @@ struct StatusView: View {
LabelIcon(iconName: "camera.aperture", value: "Viltrox 24mm F1.8 E") LabelIcon(iconName: "camera.aperture", value: "Viltrox 24mm F1.8 E")
LabelIcon(iconName: "timelapse", value: "24.0 mm, f/1.8, 1/640s, ISO 100") LabelIcon(iconName: "timelapse", value: "24.0 mm, f/1.8, 1/640s, ISO 100")
LabelIcon(iconName: "calendar", value: "2 Oct 2022") LabelIcon(iconName: "calendar", value: "2 Oct 2022")
}.padding(8)
} }
.padding(8)
.redacted(reason: .placeholder) .redacted(reason: .placeholder)
.animatePlaceholder(isLoading: .constant(true)) .animatePlaceholder(isLoading: .constant(true))
} }
} }
}
.navigationBarTitle("Details") .navigationBarTitle("Details")
.sheet(isPresented: $showCompose, content: { .sheet(isPresented: $showCompose, content: {
ComposeView() ComposeView()
@ -143,6 +154,19 @@ struct StatusView: View {
exifCreatedDate = attachmentData.exifCreatedDate exifCreatedDate = attachmentData.exifCreatedDate
exifLens = attachmentData.exifLens exifLens = attachmentData.exifLens
} }
private func getImageHeight() -> Double {
if let imageHeight = self.imageHeight, let imageWidth = self.imageWidth, imageHeight > 0 && imageWidth > 0 {
return self.calculateHeight(width: Double(imageWidth), height: Double(imageHeight))
}
return UIScreen.main.bounds.width * 0.75
}
private func calculateHeight(width: Double, height: Double) -> CGFloat {
let divider = width / UIScreen.main.bounds.size.width
return height / divider
}
} }
struct StatusView_Previews: PreviewProvider { struct StatusView_Previews: PreviewProvider {

View File

@ -20,7 +20,10 @@ struct UserProfileStatuses: View {
VStack(alignment: .center) { VStack(alignment: .center) {
if firstLoadFinished == true { if firstLoadFinished == true {
ForEach(self.statuses, id: \.id) { item in ForEach(self.statuses, id: \.id) { item in
NavigationLink(destination: StatusView(statusId: item.id) NavigationLink(destination: StatusView(statusId: item.id,
imageBlurhash: item.mediaAttachments.first?.blurhash,
imageWidth: item.getImageWidth(),
imageHeight: item.getImageHeight())
.environmentObject(applicationState)) { .environmentObject(applicationState)) {
ImageRowAsync(attachments: item.mediaAttachments) ImageRowAsync(attachments: item.mediaAttachments)
} }