diff --git a/Grain/Models/Views/PhotoModels.swift b/Grain/Models/Views/PhotoModels.swift index 5e7a451..1be6958 100644 --- a/Grain/Models/Views/PhotoModels.swift +++ b/Grain/Models/Views/PhotoModels.swift @@ -1,5 +1,16 @@ import Foundation +/// Format a Double aperture value as "f/N" with up to 2 fraction digits, dropping +/// trailing zeros. e.g., 2.0 → "f/2", 2.5 → "f/2.5", 2.83 → "f/2.83". +func formatAperture(_ value: Double) -> String { + var str = String(format: "%.2f", value) + while str.hasSuffix("0") { + str.removeLast() + } + if str.hasSuffix(".") { str.removeLast() } + return "f/" + str +} + /// social.grain.photo.defs#photoView struct GrainPhoto: Codable, Sendable, Identifiable { let uri: String @@ -45,10 +56,21 @@ struct GrainExif: Codable, Sendable { return parts.isEmpty ? nil : parts.joined(separator: " ") } + /// Re-formats the server-supplied fNumber string to drop trailing zeros. + /// "f/2.0" → "f/2", "2.0" → "f/2", "f/2.83" → "f/2.83". + var formattedFNumber: String? { + guard let fNumber else { return nil } + let cleaned = fNumber + .replacingOccurrences(of: "f/", with: "") + .trimmingCharacters(in: .whitespaces) + guard let value = Double(cleaned) else { return fNumber } + return formatAperture(value) + } + var settingsLine: String? { let parts = [ focalLengthIn35mmFormat, - fNumber, + formattedFNumber, exposureTime, iSO.map { "ISO \($0)" }, ].compactMap(\.self).filter { !$0.isEmpty }