diff --git a/PennMobile/Subletting/Listings/NewListingForm.swift b/PennMobile/Subletting/Listings/NewListingForm.swift index cab8091d8..60c63451a 100644 --- a/PennMobile/Subletting/Listings/NewListingForm.swift +++ b/PennMobile/Subletting/Listings/NewListingForm.swift @@ -11,6 +11,18 @@ import PennForms import PennMobileShared import OrderedCollections +struct URLValidator: Validator { + let message: String? = "Enter a valid URL" + + func isValid(_ input: String?) -> Bool { + guard let input, !input.isEmpty else { return true } + guard let url = URL(string: input) else { return false } + guard let scheme = url.scheme else { return true } + + return scheme.wholeMatch(of: /https?/.ignoresCase()) != nil + } +} + struct NewListingForm: View { @EnvironmentObject var navigationManager: NavigationManager @EnvironmentObject var sublettingViewModel: SublettingViewModel @@ -91,8 +103,11 @@ struct NewListingForm: View { NumericField($subletData.baths, title: "# Bath") } - TextLineField($subletData.externalLink, placeholder: "e.g. https://lauder.house.upenn.edu", title: "External Link") - .validator(.required) + ComponentWrapper { + TextLineField($subletData.externalLink, placeholder: "e.g. https://lauder.house.upenn.edu", title: "External Link") + .validator(URLValidator()) + .keyboardType(.URL) + } DateField(date: $subletData.expiresAt, title: "Listing Expiry Date") .validator(.required) @@ -170,6 +185,15 @@ struct NewListingForm: View { data.endDate = Day(date: endDate) data.amenities = Array(selectedAmenities) + if let link = data.externalLink, let url = URL(string: link) { + if url.scheme == nil { + let newUrl = "https://\(link)" + if URL(string: newUrl) != nil { + data.externalLink = newUrl + } + } + } + Task { var sublet = originalSublet diff --git a/PennMobileShared/Subletting/SublettingModels.swift b/PennMobileShared/Subletting/SublettingModels.swift index 0287b78a2..e178c6e17 100644 --- a/PennMobileShared/Subletting/SublettingModels.swift +++ b/PennMobileShared/Subletting/SublettingModels.swift @@ -85,15 +85,17 @@ public struct SubletDraft: Identifiable, Codable, Hashable { public let id: UUID public var data: SubletData public var images: [UIImage] + public var compressedImages = [UIImage: Data]() public subscript(dynamicMember keyPath: KeyPath) -> T { data[keyPath: keyPath] } - public init(id: UUID = UUID(), data: SubletData, images: [UIImage]) { + public init(id: UUID = UUID(), data: SubletData, images: [UIImage], compressedImages: [UIImage: Data] = [:]) { self.id = id self.data = data self.images = images + self.compressedImages = compressedImages } public init(from decoder: Decoder) throws { @@ -102,16 +104,24 @@ public struct SubletDraft: Identifiable, Codable, Hashable { let container = try decoder.container(keyedBy: CodingKeys.self) let id = try container.decode(UUID.self, forKey: .id) let imageData = try container.decode([Data].self, forKey: .images) - let images = imageData.compactMap { UIImage(data: $0) } - self.init(id: id, data: data, images: images) + var images = [UIImage]() + var compressedImages = [UIImage: Data]() + for imageDatum in imageData { + if let image = UIImage(data: imageDatum) { + images.append(image) + compressedImages[image] = imageDatum + } + } + + self.init(id: id, data: data, images: images, compressedImages: compressedImages) } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try data.encode(to: encoder) - let imageData = images.compactMap { $0.pngData() } + let imageData = images.compactMap { compressedImages[$0] ?? $0.jpegData(compressionQuality: 0.5) } try container.encode(imageData, forKey: .images) }