diff --git a/ec3730.xcodeproj/project.pbxproj b/ec3730.xcodeproj/project.pbxproj index 6079bc3..78f2b4c 100644 --- a/ec3730.xcodeproj/project.pbxproj +++ b/ec3730.xcodeproj/project.pbxproj @@ -42,7 +42,7 @@ F63540D5213DEBDD00605ABE /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = F63540D4213DEBDD00605ABE /* Reachability.swift */; }; F63540DD213E1D1B00605ABE /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F63540DC213E1D1B00605ABE /* StoreKit.framework */; }; F6397FE525E8937A00914B3F /* HostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6397FE425E8937900914B3F /* HostView.swift */; }; - F6397FED25EEFEAE00914B3F /* CopyCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6397FEC25EEFEAE00914B3F /* CopyCellView.swift */; }; + F6397FED25EEFEAE00914B3F /* TappedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6397FEC25EEFEAE00914B3F /* TappedText.swift */; }; F6397FF225EF012300914B3F /* ShareSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6397FF125EF012300914B3F /* ShareSheetView.swift */; }; F6397FF725EF045200914B3F /* PurchaseCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6397FF625EF045200914B3F /* PurchaseCellView.swift */; }; F63B9224279BBA03007E845F /* HostData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F63B9223279BBA03007E845F /* HostData.swift */; }; @@ -163,6 +163,19 @@ F6A4EB9D2301F1B900439562 /* WiFiSSID.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6A4EB9C2301F1B900439562 /* WiFiSSID.swift */; }; F6A6410C2844235D00E00F7F /* HostBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6A6410B2844235D00E00F7F /* HostBarView.swift */; }; F6A6410E2844246100E00F7F /* HostViewSectionFocusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6A6410D2844246100E00F7F /* HostViewSectionFocusView.swift */; }; + F6AF9576295E9C01000F09D2 /* CopyCellDetailStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6AF9575295E9C01000F09D2 /* CopyCellDetailStyle.swift */; }; + F6AF9578295E9C22000F09D2 /* CopyCellStyleConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6AF9577295E9C22000F09D2 /* CopyCellStyleConfig.swift */; }; + F6AF957A295E9C40000F09D2 /* CopyCellType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6AF9579295E9C40000F09D2 /* CopyCellType.swift */; }; + F6AF957C295E9C60000F09D2 /* NewCopyCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6AF957B295E9C60000F09D2 /* NewCopyCellProtocol.swift */; }; + F6AF957E295E9CD2000F09D2 /* CopyCellTypeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6AF957D295E9CD2000F09D2 /* CopyCellTypeView.swift */; }; + F6AF9580295E9E9E000F09D2 /* Encodable+dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6AF957F295E9E9E000F09D2 /* Encodable+dictionary.swift */; }; + F6AF9582295EA35F000F09D2 /* UIImage+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6AF9581295EA35F000F09D2 /* UIImage+Codable.swift */; }; + F6AF9584295F8627000F09D2 /* CopyCellChevronView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6AF9583295F8627000F09D2 /* CopyCellChevronView.swift */; }; + F6AF9586295F863B000F09D2 /* CopyCellContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6AF9585295F863B000F09D2 /* CopyCellContentView.swift */; }; + F6AF9588295F8695000F09D2 /* CopyCellSingleItemRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6AF9587295F8695000F09D2 /* CopyCellSingleItemRowView.swift */; }; + F6AF958A295F86AB000F09D2 /* CopyCellToggleableItemRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6AF9589295F86AB000F09D2 /* CopyCellToggleableItemRowView.swift */; }; + F6AF958C295F86D0000F09D2 /* CopyCellMultipleTypesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6AF958B295F86D0000F09D2 /* CopyCellMultipleTypesView.swift */; }; + F6AF958F295F8749000F09D2 /* PaddingListModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6AF958E295F8749000F09D2 /* PaddingListModifier.swift */; }; F6C38E8826FABEFB00BD9A71 /* StoreKitModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6C38E8726FABEFB00BD9A71 /* StoreKitModel.swift */; }; F6C591BA235EC3A9009BA4B7 /* CenterTextTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6C591B9235EC3A9009BA4B7 /* CenterTextTableViewCell.swift */; }; F6C87452234FE8030069A57C /* DataFeedsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6C87451234FE8030069A57C /* DataFeedsTableViewController.swift */; }; @@ -255,7 +268,7 @@ F63540E2213E4E7800605ABE /* Xcode.app */ = {isa = PBXFileReference; lastKnownFileType = wrapper.application; name = Xcode.app; path = ../../../../Applications/Xcode.app; sourceTree = ""; }; F63540E4213E4F8400605ABE /* libresolv.9.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libresolv.9.dylib; path = ../../../../usr/lib/libresolv.9.dylib; sourceTree = ""; }; F6397FE425E8937900914B3F /* HostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostView.swift; sourceTree = ""; }; - F6397FEC25EEFEAE00914B3F /* CopyCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyCellView.swift; sourceTree = ""; }; + F6397FEC25EEFEAE00914B3F /* TappedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TappedText.swift; sourceTree = ""; }; F6397FF125EF012300914B3F /* ShareSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheetView.swift; sourceTree = ""; }; F6397FF625EF045200914B3F /* PurchaseCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurchaseCellView.swift; sourceTree = ""; }; F63B9223279BBA03007E845F /* HostData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostData.swift; sourceTree = ""; }; @@ -334,6 +347,19 @@ F6A4EB9E2301F31100439562 /* ec3730.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ec3730.entitlements; sourceTree = ""; }; F6A6410B2844235D00E00F7F /* HostBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostBarView.swift; sourceTree = ""; }; F6A6410D2844246100E00F7F /* HostViewSectionFocusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostViewSectionFocusView.swift; sourceTree = ""; }; + F6AF9575295E9C01000F09D2 /* CopyCellDetailStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyCellDetailStyle.swift; sourceTree = ""; }; + F6AF9577295E9C22000F09D2 /* CopyCellStyleConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyCellStyleConfig.swift; sourceTree = ""; }; + F6AF9579295E9C40000F09D2 /* CopyCellType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyCellType.swift; sourceTree = ""; }; + F6AF957B295E9C60000F09D2 /* NewCopyCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCopyCellProtocol.swift; sourceTree = ""; }; + F6AF957D295E9CD2000F09D2 /* CopyCellTypeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyCellTypeView.swift; sourceTree = ""; }; + F6AF957F295E9E9E000F09D2 /* Encodable+dictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Encodable+dictionary.swift"; sourceTree = ""; }; + F6AF9581295EA35F000F09D2 /* UIImage+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Codable.swift"; sourceTree = ""; }; + F6AF9583295F8627000F09D2 /* CopyCellChevronView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyCellChevronView.swift; sourceTree = ""; }; + F6AF9585295F863B000F09D2 /* CopyCellContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyCellContentView.swift; sourceTree = ""; }; + F6AF9587295F8695000F09D2 /* CopyCellSingleItemRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyCellSingleItemRowView.swift; sourceTree = ""; }; + F6AF9589295F86AB000F09D2 /* CopyCellToggleableItemRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyCellToggleableItemRowView.swift; sourceTree = ""; }; + F6AF958B295F86D0000F09D2 /* CopyCellMultipleTypesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyCellMultipleTypesView.swift; sourceTree = ""; }; + F6AF958E295F8749000F09D2 /* PaddingListModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaddingListModifier.swift; sourceTree = ""; }; F6C38E8726FABEFB00BD9A71 /* StoreKitModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreKitModel.swift; sourceTree = ""; }; F6C591B9235EC3A9009BA4B7 /* CenterTextTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CenterTextTableViewCell.swift; sourceTree = ""; }; F6C87451234FE8030069A57C /* DataFeedsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataFeedsTableViewController.swift; sourceTree = ""; }; @@ -496,7 +522,8 @@ F660FDD62868E9590027C759 /* Device */, F644A0F9279268BD0061C98B /* FSDisclosureGroup.swift */, F660FDE928692CFB0027C759 /* WebkitOverlayView.swift */, - F6397FEC25EEFEAE00914B3F /* CopyCellView.swift */, + F6397FEC25EEFEAE00914B3F /* TappedText.swift */, + F6AF9574295E9BF1000F09D2 /* CopyCell */, F6397FF125EF012300914B3F /* ShareSheetView.swift */, F6397FF625EF045200914B3F /* PurchaseCellView.swift */, F6E53FBF287009270041A3DA /* UIViewControllerView.swift */, @@ -699,6 +726,7 @@ F60506732134B23F00DDF8A4 /* UIViewController.swift */, F6FDDFC923048DEF00FBF75E /* UIView.swift */, F6FDDFC72304803100FBF75E /* UserDefaults.swift */, + F6AF9581295EA35F000F09D2 /* UIImage+Codable.swift */, F68CE5672303291A0068D104 /* Error.swift */, F6FBB868236781AC00E023F2 /* UIDevice.swift */, F661A27F23020FCF0023FA50 /* UIAlertController.swift */, @@ -706,10 +734,36 @@ F67425DA21363FFF00A79492 /* UITextView.swift */, F63540D4213DEBDD00605ABE /* Reachability.swift */, F6E53FC528701D360041A3DA /* Interface.swift */, + F6AF957F295E9E9E000F09D2 /* Encodable+dictionary.swift */, ); path = Extensions; sourceTree = ""; }; + F6AF9574295E9BF1000F09D2 /* CopyCell */ = { + isa = PBXGroup; + children = ( + F6AF9575295E9C01000F09D2 /* CopyCellDetailStyle.swift */, + F6AF9589295F86AB000F09D2 /* CopyCellToggleableItemRowView.swift */, + F6AF958B295F86D0000F09D2 /* CopyCellMultipleTypesView.swift */, + F6AF9587295F8695000F09D2 /* CopyCellSingleItemRowView.swift */, + F6AF9585295F863B000F09D2 /* CopyCellContentView.swift */, + F6AF957D295E9CD2000F09D2 /* CopyCellTypeView.swift */, + F6AF9583295F8627000F09D2 /* CopyCellChevronView.swift */, + F6AF957B295E9C60000F09D2 /* NewCopyCellProtocol.swift */, + F6AF9579295E9C40000F09D2 /* CopyCellType.swift */, + F6AF9577295E9C22000F09D2 /* CopyCellStyleConfig.swift */, + ); + path = CopyCell; + sourceTree = ""; + }; + F6AF958D295F8737000F09D2 /* Modifiers */ = { + isa = PBXGroup; + children = ( + F6AF958E295F8749000F09D2 /* PaddingListModifier.swift */, + ); + path = Modifiers; + sourceTree = ""; + }; F6C8D898212E5AE900309373 = { isa = PBXGroup; children = ( @@ -747,6 +801,7 @@ F66643B126F02A4C000F17BC /* Models */, F6397FE325E8936600914B3F /* Views */, F668EB4A2356AE3800EEB379 /* IAPFooterView.swift */, + F6AF958D295F8737000F09D2 /* Modifiers */, F650F74A233E62C100BAA609 /* BlurredPickerView.swift */, F6FDDFC523047EE300FBF75E /* DefaultsSwitch.swift */, F68CE5612302203B0068D104 /* CopyDetailCell.swift */, @@ -1093,13 +1148,14 @@ files = ( F6FDDFC82304803100FBF75E /* UserDefaults.swift in Sources */, 2E07167328FEA629009C3844 /* WhoIsXmlContactsSectionModel.swift in Sources */, - F6397FED25EEFEAE00914B3F /* CopyCellView.swift in Sources */, + F6397FED25EEFEAE00914B3F /* TappedText.swift in Sources */, F650F74B233E62C100BAA609 /* BlurredPickerView.swift in Sources */, F6659F20287A4D8000038ECA /* SnapCalculator.swift in Sources */, F668EB4323563D2C00EEB379 /* DataFeedSubscriptionCellManager.swift in Sources */, F67425DB21363FFF00A79492 /* UITextView.swift in Sources */, F66643B526F02A8F000F17BC /* HostViewSection.swift in Sources */, 846CDA89291CF87300DDB5AA /* WhoIsXmlGeoLoactionService.swift in Sources */, + F6AF9588295F8695000F09D2 /* CopyCellSingleItemRowView.swift in Sources */, AA1595D328B24AC2003819A1 /* DataUsageInfoModel.swift in Sources */, F6A4EB972301C70000439562 /* TimedCache.swift in Sources */, F6FDDFD8230B106200FBF75E /* WhoisXmlDnsCells.swift in Sources */, @@ -1107,6 +1163,7 @@ 2E07167528FEB055009C3844 /* WhoIsXmlContactsResult.swift in Sources */, F6E53FB8286FB03F0041A3DA /* ContentView.swift in Sources */, F68CE56623031DA20068D104 /* DataFeedErrors.swift in Sources */, + F6AF9578295E9C22000F09D2 /* CopyCellStyleConfig.swift in Sources */, F660FDD82868E9A20027C759 /* DeviceInfoModel.swift in Sources */, F69E365423580A7D0017296E /* DataFeedSubscription.swift in Sources */, F69E3652235808DF0017296E /* DFOneTimePurchase.swift in Sources */, @@ -1117,11 +1174,13 @@ F6E53FBA286FB09F0041A3DA /* ScreenId.swift in Sources */, F6D0B77A235902E3009E493B /* DataFeedPurchaseProtocol.swift in Sources */, F6659F22287A4D9D00038ECA /* SnapPoint.swift in Sources */, + F6AF9582295EA35F000F09D2 /* UIImage+Codable.swift in Sources */, F661A28023020FCF0023FA50 /* UIAlertController.swift in Sources */, F63B9224279BBA03007E845F /* HostData.swift in Sources */, F66643B326F02A5B000F17BC /* HostViewModel.swift in Sources */, F63102752130C99D00F34E42 /* PingViewController.swift in Sources */, F6C38E8826FABEFB00BD9A71 /* StoreKitModel.swift in Sources */, + F6AF9586295F863B000F09D2 /* CopyCellContentView.swift in Sources */, F66643B726F030D5000F17BC /* HostSectionModel.swift in Sources */, F660FDE02868F3310027C759 /* UIDeviceInfoModel.swift in Sources */, F668EB472356616900EEB379 /* DataFeedUserApiKeyTableViewController.swift in Sources */, @@ -1135,6 +1194,7 @@ F660FDD52868E9550027C759 /* DeviceInfoView.swift in Sources */, F6C591BA235EC3A9009BA4B7 /* CenterTextTableViewCell.swift in Sources */, F6659F1D287A4CF900038ECA /* SnappedDrawer.swift in Sources */, + F6AF957E295E9CD2000F09D2 /* CopyCellTypeView.swift in Sources */, F660FDEC28692EB10027C759 /* FingerprintModel.swift in Sources */, F6659ECC287A298D00038ECA /* SourceCard.swift in Sources */, F660FDEE28693D020027C759 /* FingerprintInfoModel.swift in Sources */, @@ -1143,6 +1203,7 @@ F6E53FC928701D630041A3DA /* InterfaceView.swift in Sources */, F69E365623580AA00017296E /* DataFeedService.swift in Sources */, F68CE5622302203B0068D104 /* CopyDetailCell.swift in Sources */, + F6AF958F295F8749000F09D2 /* PaddingListModifier.swift in Sources */, F60FD36025E0607C00BCEF86 /* DataModel.xcdatamodeld in Sources */, F69E36502358089C0017296E /* DFService.swift in Sources */, F619760B22FCA76200CE39A0 /* WhoisRecord.swift in Sources */, @@ -1182,6 +1243,7 @@ F644A101279283E30061C98B /* WhoisXmlDnsSectionModel.swift in Sources */, F6A4EB922301C5B800439562 /* InAppPurchaseUpdateDelegate.swift in Sources */, F6A4EB9D2301F1B900439562 /* WiFiSSID.swift in Sources */, + F6AF9580295E9E9E000F09D2 /* Encodable+dictionary.swift in Sources */, F63540D5213DEBDD00605ABE /* Reachability.swift in Sources */, F668EB3F2355807100EEB379 /* SKProductSubscriptionPeriod.swift in Sources */, F69E364E235808560017296E /* DFSubscription.swift in Sources */, @@ -1194,10 +1256,13 @@ F660FDF0286953A00027C759 /* DeviceInfoSectionView.swift in Sources */, 2E07168128FEC3F3009C3844 /* WhoisXmlContactsService.swift in Sources */, F644A0FA279268BD0061C98B /* FSDisclosureGroup.swift in Sources */, + F6AF958A295F86AB000F09D2 /* CopyCellToggleableItemRowView.swift in Sources */, F6E53FCB287024A50041A3DA /* InterfaceConnectionBarView.swift in Sources */, 846CDA8B291CF88B00DDB5AA /* WhoIsXmlGeoLocationResult.swift in Sources */, F63102772130D76200F34E42 /* ReachabilityViewController.swift in Sources */, + F6AF958C295F86D0000F09D2 /* CopyCellMultipleTypesView.swift in Sources */, F644A0F827925CF60061C98B /* HostModelWrapperView.swift in Sources */, + F6AF957A295E9C40000F09D2 /* CopyCellType.swift in Sources */, 84CECCD5291521E500FB0F0D /* WhoIsXmlCategorizationSectionModel.swift in Sources */, F6D4BB0A23583E6100FDAFF7 /* GoogleWebRiskRecord.swift in Sources */, F69E364C235806720017296E /* WhoisXMLService.swift in Sources */, @@ -1208,9 +1273,12 @@ F660FDEA28692CFB0027C759 /* WebkitOverlayView.swift in Sources */, F6FDDFC423047E7E00FBF75E /* SettingsViewController.swift in Sources */, F6659F24287A4DD100038ECA /* SnapState.swift in Sources */, + F6AF957C295E9C60000F09D2 /* NewCopyCellProtocol.swift in Sources */, F69E364A235806370017296E /* WhoisXMLDnsService.swift in Sources */, F660FDDB2868EA8C0027C759 /* DeviceInfoSectionModel.swift in Sources */, F6FDDFE0230C4C7400FBF75E /* CellManager.swift in Sources */, + F6AF9576295E9C01000F09D2 /* CopyCellDetailStyle.swift in Sources */, + F6AF9584295F8627000F09D2 /* CopyCellChevronView.swift in Sources */, F6A6410E2844246100E00F7F /* HostViewSectionFocusView.swift in Sources */, F6397FF725EF045200914B3F /* PurchaseCellView.swift in Sources */, F63B9226279BBA87007E845F /* HostDataGroup.swift in Sources */, diff --git a/ec3730/Extensions/Encodable+dictionary.swift b/ec3730/Extensions/Encodable+dictionary.swift new file mode 100644 index 0000000..d2fc485 --- /dev/null +++ b/ec3730/Extensions/Encodable+dictionary.swift @@ -0,0 +1,16 @@ +// +// Encodable.swift +// ec3730 +// +// Created by Zachary Gorak on 12/29/22. +// Copyright © 2022 Zachary Gorak. All rights reserved. +// + +import Foundation + +extension Encodable { + var dictionary: [String: Any]? { + guard let data = try? JSONEncoder().encode(self) else { return nil } + return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] } + } +} diff --git a/ec3730/Extensions/UIImage+Codable.swift b/ec3730/Extensions/UIImage+Codable.swift new file mode 100644 index 0000000..bc50249 --- /dev/null +++ b/ec3730/Extensions/UIImage+Codable.swift @@ -0,0 +1,31 @@ +import UIKit + +enum UIImageDecodingError: Error { + case unableToCreateImage + case unableToGetImageData +} + +public extension Decodable where Self: UIImage { + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let data = try container.decode(Data.self) + if let image = Self(data: data) { + self = image + } + throw UIImageDecodingError.unableToCreateImage + } +} + +public extension Encodable where Self: UIImage { + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + if let data = pngData() { + try container.encode(data) + } else if let data = jpegData(compressionQuality: 1.0) { + try container.encode(data) + } + throw UIImageDecodingError.unableToGetImageData + } +} + +extension UIImage: Codable {} diff --git a/ec3730/Models/Device/DeviceInfoSectionModel.swift b/ec3730/Models/Device/DeviceInfoSectionModel.swift index a6fdd09..c4ed36e 100644 --- a/ec3730/Models/Device/DeviceInfoSectionModel.swift +++ b/ec3730/Models/Device/DeviceInfoSectionModel.swift @@ -3,7 +3,7 @@ import SwiftUI class DeviceInfoSectionModel: ObservableObject, Identifiable { var title: String = "" @MainActor @Published var enabled: Bool = false - @MainActor @Published var rows = [CopyCellView]() + @MainActor @Published var rows = [CopyCellType]() @MainActor func reload() {} } diff --git a/ec3730/Models/Device/Sections/CarrierInfoModel.swift b/ec3730/Models/Device/Sections/CarrierInfoModel.swift index 02aed1e..f9cae9e 100644 --- a/ec3730/Models/Device/Sections/CarrierInfoModel.swift +++ b/ec3730/Models/Device/Sections/CarrierInfoModel.swift @@ -39,29 +39,29 @@ class CarrierInfoModel: DeviceInfoSectionModel { } if let value = networkInfo.dataServiceIdentifier { - rows.append(CopyCellView(title: "Data Service Identifier", content: value)) + rows.append(.row(title: "Data Service Identifier", content: value)) } if let value = networkInfo.serviceCurrentRadioAccessTechnology { for item in value { - rows.append(CopyCellView(title: "Data Service \(item.key) Radio Access Technology", content: item.value)) + rows.append(.row(title: "Data Service \(item.key) Radio Access Technology", content: item.value)) } } for (i, carrier) in providers.enumerated() { if let name = carrier.value.carrierName { - rows.append(CopyCellView(title: "Provider \(i) Carrier Name", content: name)) + rows.append(.row(title: "Provider \(i) Carrier Name", content: name)) } - rows.append(CopyCellView(title: "Provider \(i) Service", content: carrier.key)) - rows.append(CopyCellView(title: "Provider \(i) Allows VOIP", content: carrier.value.allowsVOIP ? "Yes" : "No")) + rows.append(.row(title: "Provider \(i) Service", content: carrier.key)) + rows.append(.row(title: "Provider \(i) Allows VOIP", content: carrier.value.allowsVOIP ? "Yes" : "No")) if let value = carrier.value.isoCountryCode { - rows.append(CopyCellView(title: "Provider \(i) ISO Country Code", content: value)) + rows.append(.row(title: "Provider \(i) ISO Country Code", content: value)) } if let value = carrier.value.mobileCountryCode { - rows.append(CopyCellView(title: "Provider \(i) Mobile Country Code", content: value)) + rows.append(.row(title: "Provider \(i) Mobile Country Code", content: value)) } if let value = carrier.value.mobileNetworkCode { - rows.append(CopyCellView(title: "Provider \(i) Mobile Network Code", content: value)) + rows.append(.row(title: "Provider \(i) Mobile Network Code", content: value)) } } } diff --git a/ec3730/Models/Device/Sections/DataUsageInfoModel.swift b/ec3730/Models/Device/Sections/DataUsageInfoModel.swift index acf0247..0eee150 100644 --- a/ec3730/Models/Device/Sections/DataUsageInfoModel.swift +++ b/ec3730/Models/Device/Sections/DataUsageInfoModel.swift @@ -22,7 +22,15 @@ class DataUsageInfoModel: DeviceInfoSectionModel { enabled = true SystemDataUsage.reload() rows.removeAll() - rows.append(CopyCellView(title: "Wifi", rows: [CopyCellRow(title: "Sent", content: SystemDataUsage.wifiSent), CopyCellRow(title: "Received", content: SystemDataUsage.wifiReceived), CopyCellRow(title: "Total", content: SystemDataUsage.wifiTotal)])) - rows.append(CopyCellView(title: "Cellular", rows: [CopyCellRow(title: "Sent", content: SystemDataUsage.wwanSent), CopyCellRow(title: "Received", content: SystemDataUsage.wwanReceived), CopyCellRow(title: "Total", content: SystemDataUsage.wwanTotal)])) + rows.append(.multiple(title: "Wifi", contents: [ + .row(title: "Sent", content: SystemDataUsage.wifiSent, style: .expandable), + .row(title: "Received", content: SystemDataUsage.wifiReceived, style: .expandable), + .row(title: "Total", content: SystemDataUsage.wifiTotal, style: .expandable), + ])) + rows.append(.multiple(title: "Cellular", contents: [ + .row(title: "Sent", content: SystemDataUsage.wwanSent, style: .expandable), + .row(title: "Received", content: SystemDataUsage.wwanReceived, style: .expandable), + .row(title: "Total", content: SystemDataUsage.wwanTotal, style: .expandable), + ])) } } diff --git a/ec3730/Models/Device/Sections/FingerprintInfoModel.swift b/ec3730/Models/Device/Sections/FingerprintInfoModel.swift index f5f5d08..bc72676 100644 --- a/ec3730/Models/Device/Sections/FingerprintInfoModel.swift +++ b/ec3730/Models/Device/Sections/FingerprintInfoModel.swift @@ -26,7 +26,7 @@ class FingerprintInfoModel: DeviceInfoSectionModel { rows.removeAll() for (i, model) in models.enumerated() { - rows.append(CopyCellView(title: "Fingerprint \(i)", content: model.fingerprint ?? "-")) + rows.append(.row(title: "Fingerprint \(i)", content: model.fingerprint ?? "-")) } } } diff --git a/ec3730/Models/Device/Sections/JavaScriptInfoModel.swift b/ec3730/Models/Device/Sections/JavaScriptInfoModel.swift index 7d70703..cb98966 100644 --- a/ec3730/Models/Device/Sections/JavaScriptInfoModel.swift +++ b/ec3730/Models/Device/Sections/JavaScriptInfoModel.swift @@ -21,59 +21,59 @@ class JavaScriptInfoModel: DeviceInfoSectionModel { } if let pi = context.evaluateScript("Math.PI"), pi.isNumber { - rows.append(CopyCellView(title: "PI", content: "\(pi.toDouble())")) + rows.append(.row(title: "PI", content: "\(pi.toDouble())")) } if let value = context.evaluateScript("Math.E"), value.isNumber { - rows.append(CopyCellView(title: "Euler's constant", content: "\(value.toDouble())")) + rows.append(.row(title: "Euler's constant", content: "\(value.toDouble())")) } if let value = context.evaluateScript("Math.random()"), value.isNumber { - rows.append(CopyCellView(title: "Random", content: "\(value.toDouble())")) + rows.append(.row(title: "Random", content: "\(value.toDouble())")) } if let value = context.evaluateScript("Math.log(2)"), value.isNumber { - rows.append(CopyCellView(title: "Natural log of 2", content: "\(value.toDouble())")) + rows.append(.row(title: "Natural log of 2", content: "\(value.toDouble())")) } if let value = context.evaluateScript("Math.log(10)"), value.isNumber { - rows.append(CopyCellView(title: "Natural log of 10", content: "\(value.toDouble())")) + rows.append(.row(title: "Natural log of 10", content: "\(value.toDouble())")) } if let value = context.evaluateScript("Math.log2(10)"), value.isNumber { - rows.append(CopyCellView(title: "Base 2 logarithm of 10", content: "\(value.toDouble())")) + rows.append(.row(title: "Base 2 logarithm of 10", content: "\(value.toDouble())")) } if let value = context.evaluateScript("Math.log2(Math.E)"), value.isNumber { - rows.append(CopyCellView(title: "Base 2 logarithm of E", content: "\(value.toDouble())")) + rows.append(.row(title: "Base 2 logarithm of E", content: "\(value.toDouble())")) } if let value = context.evaluateScript("Math.sqrt(2)"), value.isNumber { - rows.append(CopyCellView(title: "Square root of 2", content: "\(value.toDouble())")) + rows.append(.row(title: "Square root of 2", content: "\(value.toDouble())")) } if let value = context.evaluateScript("Math.sqrt(1/2)"), value.isNumber { - rows.append(CopyCellView(title: "Square root of 1/2", content: "\(value.toDouble())")) + rows.append(.row(title: "Square root of 1/2", content: "\(value.toDouble())")) } if let value = context.evaluateScript("Number.MAX_SAFE_INTEGER"), value.isNumber, let str = value.toNumber()?.stringValue { - rows.append(CopyCellView(title: "Maxmimum Safe Integer", content: str)) + rows.append(.row(title: "Maxmimum Safe Integer", content: str)) } if let value = context.evaluateScript("Number.MIN_SAFE_INTEGER"), value.isNumber, let str = value.toNumber()?.stringValue { - rows.append(CopyCellView(title: "Minimum Safe Integer", content: str)) + rows.append(.row(title: "Minimum Safe Integer", content: str)) } if let value = context.evaluateScript("Number.EPSILON"), value.isNumber, let str = value.toNumber()?.stringValue { - rows.append(CopyCellView(title: "Epsilon", content: str)) + rows.append(.row(title: "Epsilon", content: str)) } if let value = context.evaluateScript("Number.MAX_VALUE"), value.isNumber { - rows.append(CopyCellView(title: "Maximum Value", content: "\(value.toDouble())")) + rows.append(.row(title: "Maximum Value", content: "\(value.toDouble())")) } if let value = context.evaluateScript("Number.MIN_VALUE"), value.isNumber { - rows.append(CopyCellView(title: "Minimum Value", content: "\(value.toDouble())")) + rows.append(.row(title: "Minimum Value", content: "\(value.toDouble())")) } } } diff --git a/ec3730/Models/Device/Sections/MemoryInfoModel.swift b/ec3730/Models/Device/Sections/MemoryInfoModel.swift index baba3b6..7209280 100644 --- a/ec3730/Models/Device/Sections/MemoryInfoModel.swift +++ b/ec3730/Models/Device/Sections/MemoryInfoModel.swift @@ -74,17 +74,17 @@ class MemoryInfoModel: DeviceInfoSectionModel { enabled = true rows.removeAll() - rows.append(CopyCellView(title: "Memory Footprint", content: formattedMemoryFootprint())) + rows.append(.row(title: "Memory Footprint", content: formattedMemoryFootprint())) let (kern_result, page_size) = ggsdf() if kern_result == KERN_SUCCESS { - rows.append(CopyCellView(title: "Page Size", content: "\(page_size) bytes")) + rows.append(.row(title: "Page Size", content: "\(page_size) bytes")) } let stats = vm_stat() for key in tags { if let val = stats[key] { - rows.append(CopyCellView(title: key, content: String(format: "%d MB", val))) + rows.append(.row(title: key, content: String(format: "%d MB", val))) } } } diff --git a/ec3730/Models/Device/Sections/ProcessInfoModel.swift b/ec3730/Models/Device/Sections/ProcessInfoModel.swift index 609ffb7..9c0cdd6 100644 --- a/ec3730/Models/Device/Sections/ProcessInfoModel.swift +++ b/ec3730/Models/Device/Sections/ProcessInfoModel.swift @@ -36,29 +36,29 @@ class ProcessInfoModel: DeviceInfoSectionModel { let info = ProcessInfo.processInfo - rows.append(CopyCellView(title: "Proces Name", content: info.processName)) - rows.append(CopyCellView(title: "Active Processors", content: "\(info.activeProcessorCount)")) - rows.append(CopyCellView(title: "Hostname", content: "\(info.hostName)")) - rows.append(CopyCellView(title: "Process Arguments", content: "\(info.arguments.joined(separator: " "))")) - rows.append(CopyCellView(title: "Low Power Mode", content: info.isLowPowerModeEnabled ? "Enabled" : "Disabled")) - rows.append(CopyCellView(title: "Physical Memory", contents: [ + rows.append(.row(title: "Proces Name", content: info.processName)) + rows.append(.row(title: "Active Processors", content: "\(info.activeProcessorCount)")) + rows.append(.row(title: "Hostname", content: "\(info.hostName)")) + rows.append(.row(title: "Process Arguments", content: "\(info.arguments.joined(separator: " "))")) + rows.append(.row(title: "Low Power Mode", content: info.isLowPowerModeEnabled ? "Enabled" : "Disabled")) + rows.append(.toggleableRow(title: "Physical Memory", contents: [ "\(info.physicalMemory) bytes", "\(String(format: "%0.1f", Double(info.physicalMemory) / 1024.0)) kiB", "\(String(format: "%0.1f", Double(info.physicalMemory) / 1024.0 / 1024.0)) MiB", "\(String(format: "%0.1f", Double(info.physicalMemory) / 1024.0 / 1024.0 / 1024.0)) GiB", ])) - rows.append(CopyCellView(title: "Globally Unique String", content: "\(info.globallyUniqueString)")) - rows.append(CopyCellView(title: "OS Version", content: info.operatingSystemVersionString)) - rows.append(CopyCellView(title: "System Uptime", content: "\(info.systemUptime)")) - rows.append(CopyCellView(title: "Is Mac Catalyst App", content: info.isMacCatalystApp ? "Yes" : "No")) - rows.append(CopyCellView(title: "Is iOS App on Mac", content: info.isiOSAppOnMac ? "Yes" : "No")) - rows.append(CopyCellView(title: "Prcoess Identifier (PID)", content: "\(info.processIdentifier)")) - rows.append(CopyCellView(title: "Thermal State", content: info.thermalState.description)) + rows.append(.row(title: "Globally Unique String", content: "\(info.globallyUniqueString)")) + rows.append(.row(title: "OS Version", content: info.operatingSystemVersionString)) + rows.append(.row(title: "System Uptime", content: "\(info.systemUptime)")) + rows.append(.row(title: "Is Mac Catalyst App", content: info.isMacCatalystApp ? "Yes" : "No")) + rows.append(.row(title: "Is iOS App on Mac", content: info.isiOSAppOnMac ? "Yes" : "No")) + rows.append(.row(title: "Prcoess Identifier (PID)", content: "\(info.processIdentifier)")) + rows.append(.row(title: "Thermal State", content: info.thermalState.description)) func getArchitecture() -> NSString { let info = NXGetLocalArchInfo() return NSString(utf8String: (info?.pointee.description)!)! } - rows.append(CopyCellView(title: "Architecture", content: getArchitecture() as String)) + rows.append(.row(title: "Architecture", content: getArchitecture() as String)) } } diff --git a/ec3730/Models/Device/Sections/UIDeviceInfoModel.swift b/ec3730/Models/Device/Sections/UIDeviceInfoModel.swift index a25442b..b579fce 100644 --- a/ec3730/Models/Device/Sections/UIDeviceInfoModel.swift +++ b/ec3730/Models/Device/Sections/UIDeviceInfoModel.swift @@ -17,77 +17,77 @@ class UIDeviceInfoModel: DeviceInfoSectionModel { enabled = true rows.removeAll() - rows.append(CopyCellView(title: "Model", content: UIDevice.current.model)) - rows.append(CopyCellView(title: "Localized Model", content: UIDevice.current.localizedModel)) - rows.append(CopyCellView(title: "Name", content: UIDevice.current.name)) - rows.append(CopyCellView(title: "System Name", content: UIDevice.current.systemName)) - rows.append(CopyCellView(title: "System Version", content: UIDevice.current.systemVersion)) - rows.append(CopyCellView(title: "UUID", content: UIDevice.current.identifierForVendor?.uuidString ?? "?")) - rows.append(CopyCellView(title: "Idiom", content: UIDevice.current.userInterfaceIdiom.description ?? "?")) - rows.append(CopyCellView(title: "Hardware Model", content: UIDevice.current.hwModel)) - rows.append(CopyCellView(title: "Hardware Machine", content: UIDevice.current.hwMachine)) + rows.append(.row(title: "Model", content: UIDevice.current.model)) + rows.append(.row(title: "Localized Model", content: UIDevice.current.localizedModel)) + rows.append(.row(title: "Name", content: UIDevice.current.name)) + rows.append(.row(title: "System Name", content: UIDevice.current.systemName)) + rows.append(.row(title: "System Version", content: UIDevice.current.systemVersion)) + rows.append(.row(title: "UUID", content: UIDevice.current.identifierForVendor?.uuidString ?? "?")) + rows.append(.row(title: "Idiom", content: UIDevice.current.userInterfaceIdiom.description ?? "?")) + rows.append(.row(title: "Hardware Model", content: UIDevice.current.hwModel)) + rows.append(.row(title: "Hardware Machine", content: UIDevice.current.hwMachine)) // "kB", "MB", "GB", "TB", "PB", "EB", "ZB", and "YB" for SI units (base 1000). // "kiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", and "YiB" for binary units (base 1024). - rows.append(CopyCellView(title: "Disk Space Available", rows: [ - CopyCellRow(title: "Important", contents: [ + rows.append(.multiple(title: "Disk Space Available", contents: [ + .toggleableRow(title: "Important", contents: [ "\(UIDevice.current.importantFreeDiskSpaceInBytes) bytes", "\(String(format: "%0.2f", Double(UIDevice.current.importantFreeDiskSpaceInBytes) / 1000.0)) kB", "\(String(format: "%0.2f", Double(UIDevice.current.importantFreeDiskSpaceInBytes) / 1000.0 / 1000.0)) MB", "\(String(format: "%0.2f", Double(UIDevice.current.importantFreeDiskSpaceInBytes) / 1000.0 / 1000.0 / 1000.0)) GB", - ]), - CopyCellRow(title: "Opportunistic", contents: [ + ], style: .expandable), + .toggleableRow(title: "Opportunistic", contents: [ "\(UIDevice.current.opportunisticFreeDiskSpaceInBytes) bytes", "\(String(format: "%0.2f", Double(UIDevice.current.opportunisticFreeDiskSpaceInBytes) / 1000.0)) kB", "\(String(format: "%0.2f", Double(UIDevice.current.opportunisticFreeDiskSpaceInBytes) / 1000.0 / 1000.0)) MB", "\(String(format: "%0.2f", Double(UIDevice.current.opportunisticFreeDiskSpaceInBytes) / 1000.0 / 1000.0 / 1000.0)) GB", - ]), - CopyCellRow(title: "Real", contents: [ + ], style: .expandable), + .toggleableRow(title: "Real", contents: [ "\(UIDevice.current.realFreeDiskSpaceInBytes) bytes", "\(String(format: "%0.2f", Double(UIDevice.current.realFreeDiskSpaceInBytes) / 1000.0)) kB", "\(String(format: "%0.2f", Double(UIDevice.current.realFreeDiskSpaceInBytes) / 1000.0 / 1000.0)) MB", "\(String(format: "%0.2f", Double(UIDevice.current.realFreeDiskSpaceInBytes) / 1000.0 / 1000.0 / 1000.0)) GB", - ]), + ], style: .expandable), ])) - rows.append(CopyCellView(title: "Volume Capacity", contents: [ + rows.append(.toggleableRow(title: "Volume Capacity", contents: [ "\(UIDevice.current.volumeCapacityInBytes) bytes", "\(String(format: "%0.2f", Double(UIDevice.current.volumeCapacityInBytes) / 1000.0)) kB", "\(String(format: "%0.2f", Double(UIDevice.current.volumeCapacityInBytes) / 1000.0 / 1000.0)) MB", "\(String(format: "%0.2f", Double(UIDevice.current.volumeCapacityInBytes) / 1000.0 / 1000.0 / 1000.0)) GB", ])) - rows.append(CopyCellView(title: "Total Disk Space", contents: [ + rows.append(.toggleableRow(title: "Total Disk Space", contents: [ "\(UIDevice.current.totalDiskSpaceInBytes) bytes", "\(String(format: "%0.2f", Double(UIDevice.current.totalDiskSpaceInBytes) / 1000.0)) kB", "\(String(format: "%0.2f", Double(UIDevice.current.totalDiskSpaceInBytes) / 1000.0 / 1000.0)) MB", "\(String(format: "%0.2f", Double(UIDevice.current.totalDiskSpaceInBytes) / 1000.0 / 1000.0 / 1000.0)) GB", ])) - rows.append(CopyCellView(title: "Supports Multitasking", content: UIDevice.current.isMultitaskingSupported ? "Yes" : "No")) + rows.append(.row(title: "Supports Multitasking", content: UIDevice.current.isMultitaskingSupported ? "Yes" : "No")) if let bootTime = UIDevice.current.boottime { - rows.append(CopyCellView(title: "Boot time", content: "\(bootTime)")) - rows.append(CopyCellView(title: "Total Uptime", content: "\(UIDevice.current.uptime)")) + rows.append(.row(title: "Boot time", content: "\(bootTime)")) + rows.append(.row(title: "Total Uptime", content: "\(UIDevice.current.uptime)")) } if UIDevice.current.batteryLevel >= 0, UIDevice.current.batteryState != .unknown, UIDevice.current.isBatteryMonitoringEnabled { - rows.append(CopyCellView(title: "Battery Level", content: "\(UIDevice.current.batteryLevel * 100)%")) - rows.append(CopyCellView(title: "Battery State", content: "\(UIDevice.current.batteryState.description ?? "?")")) + rows.append(.row(title: "Battery Level", content: "\(UIDevice.current.batteryLevel * 100)%")) + rows.append(.row(title: "Battery State", content: "\(UIDevice.current.batteryState.description ?? "?")")) } else { - rows.append(CopyCellView(title: "Battery", content: "Battery monitoring is not enabled")) + rows.append(.row(title: "Battery", content: "Battery monitoring is not enabled")) } - rows.append(CopyCellView(title: "Device", content: "\(Device.current.safeDescription)")) - rows.append(CopyCellView(title: "Supports 3D Touch", content: Device.current.has3dTouchSupport ? "Yes" : "No")) - rows.append(CopyCellView(title: "Has Biometric Sensor", content: Device.current.hasBiometricSensor ? "Yes" : "No")) - rows.append(CopyCellView(title: "Diagonal Length", content: "\(Device.current.diagonal) inches")) - rows.append(CopyCellView(title: "Brightness", content: "\(Device.current.screenBrightness)%")) - rows.append(CopyCellView(title: "Has Lidar", content: Device.current.hasLidarSensor ? "Yes" : "No")) - rows.append(CopyCellView(title: "Has Camera", content: Device.current.hasCamera ? "Yes" : "No")) - rows.append(CopyCellView(title: "Has Wide Camera", content: Device.current.hasWideCamera ? "Yes" : "No")) - rows.append(CopyCellView(title: "Has Sensor Housing", content: Device.current.hasSensorHousing ? "Yes" : "No")) - rows.append(CopyCellView(title: "Has Telephoto Camera", content: Device.current.hasTelephotoCamera ? "Yes" : "No")) - rows.append(CopyCellView(title: "Has Ultrawide Camera", content: Device.current.hasUltraWideCamera ? "Yes" : "No")) - rows.append(CopyCellView(title: "Has Rounded Display Corners", content: Device.current.hasRoundedDisplayCorners ? "Yes" : "No")) + rows.append(.row(title: "Device", content: "\(Device.current.safeDescription)")) + rows.append(.row(title: "Supports 3D Touch", content: Device.current.has3dTouchSupport ? "Yes" : "No")) + rows.append(.row(title: "Has Biometric Sensor", content: Device.current.hasBiometricSensor ? "Yes" : "No")) + rows.append(.row(title: "Diagonal Length", content: "\(Device.current.diagonal) inches")) + rows.append(.row(title: "Brightness", content: "\(Device.current.screenBrightness)%")) + rows.append(.row(title: "Has Lidar", content: Device.current.hasLidarSensor ? "Yes" : "No")) + rows.append(.row(title: "Has Camera", content: Device.current.hasCamera ? "Yes" : "No")) + rows.append(.row(title: "Has Wide Camera", content: Device.current.hasWideCamera ? "Yes" : "No")) + rows.append(.row(title: "Has Sensor Housing", content: Device.current.hasSensorHousing ? "Yes" : "No")) + rows.append(.row(title: "Has Telephoto Camera", content: Device.current.hasTelephotoCamera ? "Yes" : "No")) + rows.append(.row(title: "Has Ultrawide Camera", content: Device.current.hasUltraWideCamera ? "Yes" : "No")) + rows.append(.row(title: "Has Rounded Display Corners", content: Device.current.hasRoundedDisplayCorners ? "Yes" : "No")) } } diff --git a/ec3730/Models/HostSectionModel.swift b/ec3730/Models/HostSectionModel.swift index 31af97a..4928f9f 100644 --- a/ec3730/Models/HostSectionModel.swift +++ b/ec3730/Models/HostSectionModel.swift @@ -5,7 +5,7 @@ import SwiftUI @available(iOS 15.0, *) class HostSectionModel: ObservableObject, Equatable, Identifiable, Hashable { @MainActor - @Published var content = [CopyCellView]() + @Published var content = [CopyCellType]() @MainActor @Published var isVisible = false diff --git a/ec3730/Models/Sections/GoogleWebRiskSectionModel.swift b/ec3730/Models/Sections/GoogleWebRiskSectionModel.swift index 10f9466..9e99ba7 100644 --- a/ec3730/Models/Sections/GoogleWebRiskSectionModel.swift +++ b/ec3730/Models/Sections/GoogleWebRiskSectionModel.swift @@ -34,10 +34,10 @@ class GoogleWebRiskSectionModel: HostSectionModel { if let threats = record.threat { for threat in threats.threatTypes { - content.append(CopyCellView(title: "Risk", content: threat.description)) + content.append(.row(title: "Risk", content: threat.description)) } } else { - content.append(CopyCellView(title: "Risk", content: "None detected")) + content.append(.row(title: "Risk", content: "None detected")) } return copyData } diff --git a/ec3730/Models/Sections/LocalDnsModel.swift b/ec3730/Models/Sections/LocalDnsModel.swift index f2e2ba9..613b6f1 100644 --- a/ec3730/Models/Sections/LocalDnsModel.swift +++ b/ec3730/Models/Sections/LocalDnsModel.swift @@ -26,7 +26,7 @@ class LocalDnsModel: HostSectionModel { dataToCopy = String(data: copyData, encoding: .utf8) for address in addresses { - content.append(CopyCellView(title: "Address", content: address)) + content.append(.row(title: "Address", content: address)) } return copyData diff --git a/ec3730/Models/Sections/WhoIsXmlCategorizationSectionModel.swift b/ec3730/Models/Sections/WhoIsXmlCategorizationSectionModel.swift index fa60b2a..0cd014c 100644 --- a/ec3730/Models/Sections/WhoIsXmlCategorizationSectionModel.swift +++ b/ec3730/Models/Sections/WhoIsXmlCategorizationSectionModel.swift @@ -26,24 +26,24 @@ class WhoIsXmlCategorizationSectionModel: HostSectionModel { if let categories = records.categories { for (index, category) in categories.enumerated() { - var rows = [CopyCellRow]() + var rows = [CopyCellType]() if let tier1 = category.tier1 { let name = tier1.name ?? "" let confidence = tier1.confidence ?? 0.0 let id = tier1.id ?? "" - rows.append(CopyCellRow(title: "Tier1", content: "Name - \(name)\n Id - \(id)\n Confidence - \(confidence)")) + rows.append(.row(title: "Tier1", content: "Name - \(name)\n Id - \(id)\n Confidence - \(confidence)", style: .expandable)) } if let tier2 = category.tier2 { let name = tier2.name ?? "" let confidence = tier2.confidence ?? 0.0 let id = tier2.id ?? "" - rows.append(CopyCellRow(title: "Tier2", content: "Name - \(name)\n Id - \(id)\n Confidence - \(confidence)")) + rows.append(.row(title: "Tier2", content: "Name - \(name)\n Id - \(id)\n Confidence - \(confidence)", style: .expandable)) } if !rows.isEmpty { - content.append(CopyCellView(title: "Category \(index + 1)", rows: rows)) + content.append(.multiple(title: "Category \(index + 1)", contents: rows)) } } } diff --git a/ec3730/Models/Sections/WhoIsXmlContactsSectionModel.swift b/ec3730/Models/Sections/WhoIsXmlContactsSectionModel.swift index 6b29add..1a78ee6 100644 --- a/ec3730/Models/Sections/WhoIsXmlContactsSectionModel.swift +++ b/ec3730/Models/Sections/WhoIsXmlContactsSectionModel.swift @@ -27,33 +27,35 @@ class WhoIsXmlContactsSectionModel: HostSectionModel { if let names = records.companyNames, !names.isEmpty { if names.count > 1 { - let row = CopyCellView(title: "Company Names", rows: names.map { CopyCellRow(content: $0) }) + let row = CopyCellType.multiple(title: "Company Names", contents: names.compactMap { .content($0, style: .expandable) }) content.append(row) } else if names.count == 1 { - let row = CopyCellView(title: "Company Name", content: names[0]) + let row = CopyCellType.row(title: "Company Name", content: names[0]) content.append(row) } } if let title = records.meta?.title { - content.append(CopyCellView(title: "Title", content: title)) + content.append(.row(title: "Title", content: title)) } if let value = records.meta?.metaDescription, !value.isEmpty { - content.append(CopyCellView(title: "Description", content: value)) + content.append(.row(title: "Description", content: value)) } if let postal = records.postalAddresses { if postal.count > 1 { - let row = CopyCellView(title: "Postal Addresses", rows: postal.map { CopyCellRow(content: $0) }) + let row = CopyCellType.multiple(title: "Postal Addresses", contents: postal.map { .content($0, style: .expandable) }) content.append(row) } else if postal.count == 1 { - let row = CopyCellView(title: "Postal Address", content: postal[0]) + let row = CopyCellType.row(title: "Postal Address", content: postal[0]) content.append(row) } } - content.append(CopyCellView(title: "Country code", content: records.countryCode)) + if let countryCode = records.countryCode { + content.append(.row(title: "Country code", content: countryCode)) + } if let emails = records.emails { var emailsArr = [String]() @@ -62,10 +64,10 @@ class WhoIsXmlContactsSectionModel: HostSectionModel { } if emailsArr.count > 1 { - let row = CopyCellView(title: "Emails", rows: emailsArr.map { CopyCellRow(content: $0) }) + let row = CopyCellType.multiple(title: "Emails", contents: emailsArr.map { .content($0, style: .expandable) }) content.append(row) } else if emailsArr.count == 1 { - let row = CopyCellView(title: "Email", content: emailsArr[0]) + let row = CopyCellType.row(title: "Email", content: emailsArr[0]) content.append(row) } } @@ -78,38 +80,40 @@ class WhoIsXmlContactsSectionModel: HostSectionModel { } if phoneArr.count > 1 { - let row = CopyCellView(title: "Phone Numbers", rows: phoneArr.map { CopyCellRow(content: $0) }) + let row = CopyCellType.multiple(title: "Phone Numbers", contents: phoneArr.map { .content($0, style: .expandable) }) content.append(row) } else if phoneArr.count == 1 { - let row = CopyCellView(title: "Phone Number", content: phoneArr[0]) + let row = CopyCellType.row(title: "Phone Number", content: phoneArr[0]) content.append(row) } } - content.append(CopyCellView(title: "Domain name", content: records.domainName)) + if let domainName = records.domainName { + content.append(.row(title: "Domain name", content: domainName)) + } - content.append(CopyCellView(title: "Website responed", content: "\(records.websiteResponded ?? false)")) + content.append(.row(title: "Website responed", content: "\(records.websiteResponded ?? false)")) - var socialRows = [CopyCellRow]() + var socialRows = [CopyCellType]() if let facebook = records.socialLinks?.facebook, !facebook.isEmpty { - socialRows.append(CopyCellRow(title: "Facebook", content: facebook)) + socialRows.append(.row(title: "Facebook", content: facebook, style: .expandable)) } if let twitter = records.socialLinks?.twitter, !twitter.isEmpty { - socialRows.append(CopyCellRow(title: "Twitter", content: twitter)) + socialRows.append(.row(title: "Twitter", content: twitter, style: .expandable)) } if let instagram = records.socialLinks?.instagram, !instagram.isEmpty { - socialRows.append(CopyCellRow(title: "Instagram", content: instagram)) + socialRows.append(.row(title: "Instagram", content: instagram, style: .expandable)) } if let linkedIn = records.socialLinks?.linkedIn, !linkedIn.isEmpty { - socialRows.append(CopyCellRow(title: "LinkedIn", content: linkedIn)) + socialRows.append(.row(title: "LinkedIn", content: linkedIn, style: .expandable)) } if !socialRows.isEmpty { - content.append(CopyCellView(title: "Social Links", rows: socialRows)) + content.append(.multiple(title: "Social Links", contents: socialRows)) } return copyData diff --git a/ec3730/Models/Sections/WhoIsXmlGeoLocationSectionModel.swift b/ec3730/Models/Sections/WhoIsXmlGeoLocationSectionModel.swift index baa4915..c495aae 100644 --- a/ec3730/Models/Sections/WhoIsXmlGeoLocationSectionModel.swift +++ b/ec3730/Models/Sections/WhoIsXmlGeoLocationSectionModel.swift @@ -25,85 +25,85 @@ class WhoIsXmlGeoLocationSectionModel: HostSectionModel { dataToCopy = String(data: copyData, encoding: .utf8) if let ip = records.ip, !ip.isEmpty { - let row = CopyCellView(title: "IP", content: ip) + let row = CopyCellType.row(title: "IP", content: ip) content.append(row) } if let isp = records.isp, !isp.isEmpty { - let row = CopyCellView(title: "Isp", content: isp) + let row = CopyCellType.row(title: "Isp", content: isp) content.append(row) } if let domains = records.domains, !domains.isEmpty { if domains.count > 1 { - let row = CopyCellView(title: "Domains", rows: domains.map { CopyCellRow(content: $0) }) + let row = CopyCellType.multiple(title: "Domains", contents: domains.map { .content($0, style: .expandable) }) content.append(row) } else if domains.count == 1 { - let row = CopyCellView(title: "Domain", content: domains[0]) + let row = CopyCellType.row(title: "Domain", content: domains[0]) content.append(row) } } - var locationRows = [CopyCellRow]() + var locationRows = [CopyCellType]() if let country = records.location?.country, !country.isEmpty { - locationRows.append(CopyCellRow(title: "Country", content: country)) + locationRows.append(.row(title: "Country", content: country, style: .expandable)) } if let region = records.location?.region, !region.isEmpty { - locationRows.append(CopyCellRow(title: "Region", content: region)) + locationRows.append(.row(title: "Region", content: region, style: .expandable)) } if let city = records.location?.city, !city.isEmpty { - locationRows.append(CopyCellRow(title: "City", content: city)) + locationRows.append(.row(title: "City", content: city, style: .expandable)) } if let lat = records.location?.lat { - locationRows.append(CopyCellRow(title: "Latitude", content: "\(lat)")) + locationRows.append(.row(title: "Latitude", content: "\(lat)", style: .expandable)) } if let lng = records.location?.lng { - locationRows.append(CopyCellRow(title: "Longitude", content: "\(lng)")) + locationRows.append(.row(title: "Longitude", content: "\(lng)", style: .expandable)) } if let postalCode = records.location?.postalCode, !postalCode.isEmpty { - locationRows.append(CopyCellRow(title: "Postal Code", content: postalCode)) + locationRows.append(.row(title: "Postal Code", content: postalCode, style: .expandable)) } if let timezone = records.location?.timezone, !timezone.isEmpty { - locationRows.append(CopyCellRow(title: "Timezone", content: timezone)) + locationRows.append(.row(title: "Timezone", content: timezone, style: .expandable)) } if let geonameId = records.location?.geonameID, geonameId != 0 { - locationRows.append(CopyCellRow(title: "GeonameId", content: "\(geonameId)")) + locationRows.append(.row(title: "GeonameId", content: "\(geonameId)", style: .expandable)) } if !locationRows.isEmpty { - content.append(CopyCellView(title: "Location", rows: locationRows)) + content.append(.multiple(title: "Location", contents: locationRows)) } if let geoLocationModelClassAs = records.geoLocationModelClassAs { - var geoLocationAsRows = [CopyCellRow]() + var geoLocationAsRows = [CopyCellType]() if let asn = geoLocationModelClassAs.asn { - geoLocationAsRows.append(CopyCellRow(title: "Asn", content: "\(asn)")) + geoLocationAsRows.append(.row(title: "Asn", content: "\(asn)", style: .expandable)) } if let name = geoLocationModelClassAs.name, !name.isEmpty { - geoLocationAsRows.append(CopyCellRow(title: "Name", content: name)) + geoLocationAsRows.append(.row(title: "Name", content: name, style: .expandable)) } if let domain = geoLocationModelClassAs.domain, !domain.isEmpty { - geoLocationAsRows.append(CopyCellRow(title: "Domain", content: domain)) + geoLocationAsRows.append(.row(title: "Domain", content: domain, style: .expandable)) } if let route = geoLocationModelClassAs.route, !route.isEmpty { - geoLocationAsRows.append(CopyCellRow(title: "Route", content: "\(route)")) + geoLocationAsRows.append(.row(title: "Route", content: "\(route)", style: .expandable)) } if let type = geoLocationModelClassAs.type, !type.isEmpty { - geoLocationAsRows.append(CopyCellRow(title: "Type", content: "\(type)")) + geoLocationAsRows.append(.row(title: "Type", content: "\(type)", style: .expandable)) } if !geoLocationAsRows.isEmpty { - content.append(CopyCellView(title: "GeoLocationAsRows", rows: geoLocationAsRows)) + content.append(.multiple(title: "GeoLocationAsRows", contents: geoLocationAsRows)) } } diff --git a/ec3730/Models/Sections/WhoisXmlDnsSectionModel.swift b/ec3730/Models/Sections/WhoisXmlDnsSectionModel.swift index b8cc7df..101ebae 100644 --- a/ec3730/Models/Sections/WhoisXmlDnsSectionModel.swift +++ b/ec3730/Models/Sections/WhoisXmlDnsSectionModel.swift @@ -25,44 +25,44 @@ class WhoisXmlDnsSectionModel: HostSectionModel { dataToCopy = String(data: copyData, encoding: .utf8) for record in records { - var rows = [CopyCellRow]() + var rows = [CopyCellType]() - rows.append(CopyCellRow(title: "name", content: record.name)) - rows.append(CopyCellRow(title: "ttl", content: "\(record.ttl)")) - rows.append(CopyCellRow(title: "RRset Type", content: "\(record.rRsetType)")) + rows.append(.row(title: "name", content: record.name, style: .expandable)) + rows.append(.row(title: "ttl", content: "\(record.ttl)", style: .expandable)) + rows.append(.row(title: "RRset Type", content: "\(record.rRsetType)", style: .expandable)) if let admin = record.admin { - rows.append(CopyCellRow(title: "Admin", content: admin)) + rows.append(.row(title: "Admin", content: admin, style: .expandable)) } if let host = record.host { - rows.append(CopyCellRow(title: "Host", content: host)) + rows.append(.row(title: "Host", content: host, style: .expandable)) } if let address = record.address { - rows.append(CopyCellRow(title: "Address", content: address)) + rows.append(.row(title: "Address", content: address, style: .expandable)) } if let strings = record.strings { - let row = CopyCellRow(title: "Strings", content: strings.joined(separator: "\n")) + let row = CopyCellType.row(title: "Strings", content: strings.joined(separator: "\n"), style: .expandable) rows.append(row) } if let expire = record.expire { - rows.append(CopyCellRow(title: "Expire", content: "\(expire)")) + rows.append(.row(title: "Expire", content: "\(expire)", style: .expandable)) } if let value = record.minimum { - rows.append(CopyCellRow(title: "Minimum", content: "\(value)")) + rows.append(.row(title: "Minimum", content: "\(value)", style: .expandable)) } if let value = record.refresh { - rows.append(CopyCellRow(title: "Refresh", content: "\(value)")) + rows.append(.row(title: "Refresh", content: "\(value)", style: .expandable)) } if let value = record.retry { - rows.append(CopyCellRow(title: "Retry", content: "\(value)")) + rows.append(.row(title: "Retry", content: "\(value)", style: .expandable)) } if let value = record.serial { - rows.append(CopyCellRow(title: "Serial", content: "\(value)")) + rows.append(.row(title: "Serial", content: "\(value)", style: .expandable)) } - rows.append(CopyCellRow(title: "Raw", content: record.rawText)) + rows.append(.row(title: "Raw", content: record.rawText, style: .expandable)) - content.append(CopyCellView(title: record.dnsType, rows: rows)) + content.append(.multiple(title: record.dnsType, contents: rows)) } return copyData diff --git a/ec3730/Models/Sections/WhoisXmlReputationSectionModel.swift b/ec3730/Models/Sections/WhoisXmlReputationSectionModel.swift index ace3f4d..c21c739 100644 --- a/ec3730/Models/Sections/WhoisXmlReputationSectionModel.swift +++ b/ec3730/Models/Sections/WhoisXmlReputationSectionModel.swift @@ -25,22 +25,22 @@ class WhoisXmlReputationSectionModel: HostSectionModel { dataToCopy = String(data: copyData, encoding: .utf8) if let score = record.reputationScore { - content.append(CopyCellView(title: "Score", content: "\(score)")) + content.append(.row(title: "Score", content: "\(score)")) } else { - content.append(CopyCellView(title: "Score", content: "-")) + content.append(.row(title: "Score", content: "-")) } - content.append(CopyCellView(title: "Mode", content: "\(record.mode ?? "-")")) + content.append(.row(title: "Mode", content: "\(record.mode ?? "-")")) guard let tests = record.testResults else { return copyData } for test in tests { - var rows = [CopyCellRow]() + var rows = [CopyCellType]() for warning in test.warnings { - rows.append(CopyCellRow(title: "", content: warning)) + rows.append(.row(title: "", content: warning, style: .expandable)) } - content.append(CopyCellView(title: test.test, rows: rows)) + content.append(.multiple(title: test.test, contents: rows)) } return copyData diff --git a/ec3730/Models/Sections/WhoisXmlWhoisSectionModel.swift b/ec3730/Models/Sections/WhoisXmlWhoisSectionModel.swift index a887b03..117d107 100644 --- a/ec3730/Models/Sections/WhoisXmlWhoisSectionModel.swift +++ b/ec3730/Models/Sections/WhoisXmlWhoisSectionModel.swift @@ -24,49 +24,51 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { dataToCopy = String(data: copyData, encoding: .utf8) if let error = record.dataError { - content.append(CopyCellView(title: "Error", content: error)) + content.append(.row(title: "Error", content: error)) } - content.append(CopyCellView(title: "Created", content: "\(record.createdDate ?? record.registryData.createdDate ?? record.audit.createdDate)")) + content.append(.row(title: "Created", content: "\(record.createdDate ?? record.registryData.createdDate ?? record.audit.createdDate)")) - content.append(CopyCellView(title: "Updated", content: "\(record.updatedDate ?? record.registryData.updatedDate ?? record.audit.updatedDate)")) + content.append(.row(title: "Updated", content: "\(record.updatedDate ?? record.registryData.updatedDate ?? record.audit.updatedDate)")) if let expiresDate = record.expiresDate ?? record.registryData.expiresDate { - content.append(CopyCellView(title: "Expires", content: "\(expiresDate)")) + content.append(.row(title: "Expires", content: "\(expiresDate)")) } - content.append(CopyCellView(title: "Registrar", content: record.registrarName)) + if let registrarName = record.registrarName { + content.append(.row(title: "Registrar", content: registrarName)) + } - content.append(CopyCellView(title: "IANAID", content: record.registrarIANAID)) + if let registrarIANAID = record.registrarIANAID { + content.append(.row(title: "IANAID", content: registrarIANAID)) + } if let whoisServer = record.whoisServer { - content.append(CopyCellView(title: "WHOIS Server", content: whoisServer)) + content.append(.row(title: "WHOIS Server", content: whoisServer)) } if let estimatedAge = record.estimatedDomainAge { - content.append(CopyCellView(title: "Estimated Age", content: "\(estimatedAge) day(s)")) + content.append(.row(title: "Estimated Age", content: "\(estimatedAge) day(s)")) } - content.append(CopyCellView(title: "Contact Email", content: record.contactEmail)) + if let contactEmail = record.contactEmail { + content.append(.row(title: "Contact Email", content: contactEmail)) + } let hostNames = record.nameServers?.hostNames ?? record.registryData.nameServers?.hostNames ?? [] if !hostNames.isEmpty { - var cells = [CopyCellRow]() - for host in hostNames.sorted() { - cells.append(CopyCellRow(title: nil, content: host)) - } - content.append(CopyCellView(title: "Host Names", rows: cells)) + content.append(.multiple(title: "Host Names", contents: hostNames.sorted().map { .content($0, style: .expandable) })) } if let contact = record.registrant { - var rows = [CopyCellRow]() + var rows = [CopyCellType]() if let name = contact.name { - rows.append(CopyCellRow(title: "Name", content: name)) + rows.append(.row(title: "Name", content: name, style: .expandable)) } if let org = contact.organization { - rows.append(CopyCellRow(title: "Organization", content: org)) + rows.append(.row(title: "Organization", content: org, style: .expandable)) } var street = [String]() @@ -83,31 +85,31 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { street.append(address) } if !street.isEmpty { - rows.append(CopyCellRow(title: "Street", content: street.joined(separator: "\n"))) + rows.append(.row(title: "Street", content: street.joined(separator: "\n"), style: .expandable)) } if let city = contact.city { - rows.append(CopyCellRow(title: "City", content: city)) + rows.append(.row(title: "City", content: city, style: .expandable)) } if let postCode = contact.postalCode { - rows.append(CopyCellRow(title: "Postal Code", content: postCode)) + rows.append(.row(title: "Postal Code", content: postCode, style: .expandable)) } if let state = contact.state { - rows.append(CopyCellRow(title: "State", content: state)) + rows.append(.row(title: "State", content: state, style: .expandable)) } if let country = contact.country { if let countryCode = contact.countryCode { - rows.append(CopyCellRow(title: "Country", content: country + "(\(countryCode))")) + rows.append(.row(title: "Country", content: country + "(\(countryCode))", style: .expandable)) } else { - rows.append(CopyCellRow(title: "Country", content: country)) + rows.append(.row(title: "Country", content: country, style: .expandable)) } } if let email = contact.email { - rows.append(CopyCellRow(title: "Email", content: email)) + rows.append(.row(title: "Email", content: email, style: .expandable)) } if var phone = contact.telephone { @@ -115,7 +117,7 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { phone += " \(phoneExt)" } - rows.append(CopyCellRow(title: "Fax", content: phone)) + rows.append(.row(title: "Fax", content: phone, style: .expandable)) } if var fax = contact.fax { @@ -123,19 +125,19 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { fax += " \(faxExt)" } - rows.append(CopyCellRow(title: "Fax", content: fax)) + rows.append(.row(title: "Fax", content: fax, style: .expandable)) } - content.append(CopyCellView(title: "Registrant", rows: rows)) + content.append(.multiple(title: "Registrant", contents: rows)) } else if let contact = record.registryData.regustrant { - var rows = [CopyCellRow]() + var rows = [CopyCellType]() if let name = contact.name { - rows.append(CopyCellRow(title: "Name", content: name)) + rows.append(.row(title: "Name", content: name, style: .expandable)) } if let org = contact.organization { - rows.append(CopyCellRow(title: "Organization", content: org)) + rows.append(.row(title: "Organization", content: org, style: .expandable)) } var street = [String]() @@ -152,32 +154,32 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { street.append(address) } if !street.isEmpty { - rows.append(CopyCellRow(title: "Street", content: street.joined(separator: "\n"))) + rows.append(.row(title: "Street", content: street.joined(separator: "\n"), style: .expandable)) } if let city = contact.city { - rows.append(CopyCellRow(title: "City", content: city)) + rows.append(.row(title: "City", content: city, style: .expandable)) } if let postCode = contact.postalCode { - rows.append(CopyCellRow(title: "Postal Code", content: postCode)) + rows.append(.row(title: "Postal Code", content: postCode, style: .expandable)) } if let state = contact.state { - rows.append(CopyCellRow(title: "State", content: state)) + rows.append(.row(title: "State", content: state, style: .expandable)) } - content.append(CopyCellView(title: "Registrant", rows: rows)) + content.append(.multiple(title: "Registrant", contents: rows)) } if let contact = record.administrativeContact { - var rows = [CopyCellRow]() + var rows = [CopyCellType]() if let name = contact.name { - rows.append(CopyCellRow(title: "Name", content: name)) + rows.append(.row(title: "Name", content: name, style: .expandable)) } if let org = contact.organization { - rows.append(CopyCellRow(title: "Organization", content: org)) + rows.append(.row(title: "Organization", content: org, style: .expandable)) } var street = [String]() @@ -194,31 +196,31 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { street.append(address) } if !street.isEmpty { - rows.append(CopyCellRow(title: "Street", content: street.joined(separator: "\n"))) + rows.append(.row(title: "Street", content: street.joined(separator: "\n"), style: .expandable)) } if let city = contact.city { - rows.append(CopyCellRow(title: "City", content: city)) + rows.append(.row(title: "City", content: city, style: .expandable)) } if let postCode = contact.postalCode { - rows.append(CopyCellRow(title: "Postal Code", content: postCode)) + rows.append(.row(title: "Postal Code", content: postCode, style: .expandable)) } if let state = contact.state { - rows.append(CopyCellRow(title: "State", content: state)) + rows.append(.row(title: "State", content: state, style: .expandable)) } if let country = contact.country { if let countryCode = contact.countryCode { - rows.append(CopyCellRow(title: "Country", content: "\(country) (\(countryCode))")) + rows.append(.row(title: "Country", content: "\(country) (\(countryCode))", style: .expandable)) } else { - rows.append(CopyCellRow(title: "Country", content: country)) + rows.append(.row(title: "Country", content: country, style: .expandable)) } } if let email = contact.email { - rows.append(CopyCellRow(title: "Email", content: email)) + rows.append(.row(title: "Email", content: email, style: .expandable)) } if var phone = contact.telephone { @@ -226,7 +228,7 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { phone += " \(phoneExt)" } - rows.append(CopyCellRow(title: "Fax", content: phone)) + rows.append(.row(title: "Fax", content: phone, style: .expandable)) } if var fax = contact.fax { @@ -234,18 +236,18 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { fax += " \(faxExt)" } - rows.append(CopyCellRow(title: "Fax", content: fax)) + rows.append(.row(title: "Fax", content: fax, style: .expandable)) } - content.append(CopyCellView(title: "Administrative Contact", rows: rows)) + content.append(.multiple(title: "Administrative Contact", contents: rows)) } else if let contact = record.registryData.administrativeContact { - var rows = [CopyCellRow]() + var rows = [CopyCellType]() if let name = contact.name { - rows.append(CopyCellRow(title: "Name", content: name)) + rows.append(.row(title: "Name", content: name, style: .expandable)) } if let org = contact.organization { - rows.append(CopyCellRow(title: "Organization", content: org)) + rows.append(.row(title: "Organization", content: org, style: .expandable)) } var street = [String]() @@ -262,31 +264,31 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { street.append(address) } if !street.isEmpty { - rows.append(CopyCellRow(title: "Street", content: street.joined(separator: "\n"))) + rows.append(.row(title: "Street", content: street.joined(separator: "\n"), style: .expandable)) } if let city = contact.city { - rows.append(CopyCellRow(title: "City", content: city)) + rows.append(.row(title: "City", content: city, style: .expandable)) } if let postCode = contact.postalCode { - rows.append(CopyCellRow(title: "Postal Code", content: postCode)) + rows.append(.row(title: "Postal Code", content: postCode, style: .expandable)) } if let state = contact.state { - rows.append(CopyCellRow(title: "State", content: state)) + rows.append(.row(title: "State", content: state, style: .expandable)) } if let country = contact.country { if let countryCode = contact.countryCode { - rows.append(CopyCellRow(title: "Country", content: country + "(\(countryCode))")) + rows.append(.row(title: "Country", content: country + "(\(countryCode))", style: .expandable)) } else { - rows.append(CopyCellRow(title: "Country", content: country)) + rows.append(.row(title: "Country", content: country, style: .expandable)) } } if let email = contact.email { - rows.append(CopyCellRow(title: "Email", content: email)) + rows.append(.row(title: "Email", content: email, style: .expandable)) } if var phone = contact.telephone { @@ -294,7 +296,7 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { phone += " \(phoneExt)" } - rows.append(CopyCellRow(title: "Fax", content: phone)) + rows.append(.row(title: "Fax", content: phone, style: .expandable)) } if var fax = contact.fax { @@ -302,20 +304,20 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { fax += " \(faxExt)" } - rows.append(CopyCellRow(title: "Fax", content: fax)) + rows.append(.row(title: "Fax", content: fax, style: .expandable)) } - content.append(CopyCellView(title: "Administrative Contact", rows: rows)) + content.append(.multiple(title: "Administrative Contact", contents: rows)) } // if let contact = record.technicalContact { - var rows = [CopyCellRow]() + var rows = [CopyCellType]() if let name = contact.name { - rows.append(CopyCellRow(title: "Name", content: name)) + rows.append(.row(title: "Name", content: name, style: .expandable)) } if let org = contact.organization { - rows.append(CopyCellRow(title: "Organization", content: org)) + rows.append(.row(title: "Organization", content: org, style: .expandable)) } var street = [String]() @@ -332,31 +334,31 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { street.append(address) } if !street.isEmpty { - rows.append(CopyCellRow(title: "Street", content: street.joined(separator: "\n"))) + rows.append(.row(title: "Street", content: street.joined(separator: "\n"), style: .expandable)) } if let city = contact.city { - rows.append(CopyCellRow(title: "City", content: city)) + rows.append(.row(title: "City", content: city, style: .expandable)) } if let postCode = contact.postalCode { - rows.append(CopyCellRow(title: "Postal Code", content: postCode)) + rows.append(.row(title: "Postal Code", content: postCode, style: .expandable)) } if let state = contact.state { - rows.append(CopyCellRow(title: "State", content: state)) + rows.append(.row(title: "State", content: state, style: .expandable)) } if let country = contact.country { if let countryCode = contact.countryCode { - rows.append(CopyCellRow(title: "Country", content: country + "(\(countryCode))")) + rows.append(.row(title: "Country", content: country + "(\(countryCode))", style: .expandable)) } else { - rows.append(CopyCellRow(title: "Country", content: country)) + rows.append(.row(title: "Country", content: country, style: .expandable)) } } if let email = contact.email { - rows.append(CopyCellRow(title: "Email", content: email)) + rows.append(.row(title: "Email", content: email, style: .expandable)) } if var phone = contact.telephone { @@ -364,7 +366,7 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { phone += " \(phoneExt)" } - rows.append(CopyCellRow(title: "Fax", content: phone)) + rows.append(.row(title: "Fax", content: phone, style: .expandable)) } if var fax = contact.fax { @@ -372,18 +374,18 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { fax += " \(faxExt)" } - rows.append(CopyCellRow(title: "Fax", content: fax)) + rows.append(.row(title: "Fax", content: fax, style: .expandable)) } - content.append(CopyCellView(title: "Technical Contact", rows: rows)) + content.append(.multiple(title: "Technical Contact", contents: rows)) } else if let contact = record.registryData.technicalContact { - var rows = [CopyCellRow]() + var rows = [CopyCellType]() if let name = contact.name { - rows.append(CopyCellRow(title: "Name", content: name)) + rows.append(.row(title: "Name", content: name, style: .expandable)) } if let org = contact.organization { - rows.append(CopyCellRow(title: "Organization", content: org)) + rows.append(.row(title: "Organization", content: org, style: .expandable)) } var street = [String]() @@ -400,31 +402,31 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { street.append(address) } if !street.isEmpty { - rows.append(CopyCellRow(title: "Street", content: street.joined(separator: "\n"))) + rows.append(.row(title: "Street", content: street.joined(separator: "\n"), style: .expandable)) } if let city = contact.city { - rows.append(CopyCellRow(title: "City", content: city)) + rows.append(.row(title: "City", content: city, style: .expandable)) } if let postCode = contact.postalCode { - rows.append(CopyCellRow(title: "Postal Code", content: postCode)) + rows.append(.row(title: "Postal Code", content: postCode, style: .expandable)) } if let state = contact.state { - rows.append(CopyCellRow(title: "State", content: state)) + rows.append(.row(title: "State", content: state, style: .expandable)) } if let country = contact.country { if let countryCode = contact.countryCode { - rows.append(CopyCellRow(title: "Country", content: country + "(\(countryCode))")) + rows.append(.row(title: "Country", content: country + "(\(countryCode))", style: .expandable)) } else { - rows.append(CopyCellRow(title: "Country", content: country)) + rows.append(.row(title: "Country", content: country, style: .expandable)) } } if let email = contact.email { - rows.append(CopyCellRow(title: "Email", content: email)) + rows.append(.row(title: "Email", content: email, style: .expandable)) } if var phone = contact.telephone { @@ -432,7 +434,7 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { phone += " \(phoneExt)" } - rows.append(CopyCellRow(title: "Fax", content: phone)) + rows.append(.row(title: "Fax", content: phone, style: .expandable)) } if var fax = contact.fax { @@ -440,20 +442,20 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { fax += " \(faxExt)" } - rows.append(CopyCellRow(title: "Fax", content: fax)) + rows.append(.row(title: "Fax", content: fax, style: .expandable)) } - content.append(CopyCellView(title: "Technical Contact", rows: rows)) + content.append(.multiple(title: "Technical Contact", contents: rows)) } // if let contact = record.billingContact { - var rows = [CopyCellRow]() + var rows = [CopyCellType]() if let name = contact.name { - rows.append(CopyCellRow(title: "Name", content: name)) + rows.append(.row(title: "Name", content: name, style: .expandable)) } if let org = contact.organization { - rows.append(CopyCellRow(title: "Organization", content: org)) + rows.append(.row(title: "Organization", content: org, style: .expandable)) } var street = [String]() @@ -470,31 +472,31 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { street.append(address) } if !street.isEmpty { - rows.append(CopyCellRow(title: "Street", content: street.joined(separator: "\n"))) + rows.append(.row(title: "Street", content: street.joined(separator: "\n"), style: .expandable)) } if let city = contact.city { - rows.append(CopyCellRow(title: "City", content: city)) + rows.append(.row(title: "City", content: city, style: .expandable)) } if let postCode = contact.postalCode { - rows.append(CopyCellRow(title: "Postal Code", content: postCode)) + rows.append(.row(title: "Postal Code", content: postCode, style: .expandable)) } if let state = contact.state { - rows.append(CopyCellRow(title: "State", content: state)) + rows.append(.row(title: "State", content: state, style: .expandable)) } if let country = contact.country { if let countryCode = contact.countryCode { - rows.append(CopyCellRow(title: "Country", content: country + "(\(countryCode))")) + rows.append(.row(title: "Country", content: country + "(\(countryCode))", style: .expandable)) } else { - rows.append(CopyCellRow(title: "Country", content: country)) + rows.append(.row(title: "Country", content: country, style: .expandable)) } } if let email = contact.email { - rows.append(CopyCellRow(title: "Email", content: email)) + rows.append(.row(title: "Email", content: email, style: .expandable)) } if var phone = contact.telephone { @@ -502,7 +504,7 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { phone += " \(phoneExt)" } - rows.append(CopyCellRow(title: "Fax", content: phone)) + rows.append(.row(title: "Fax", content: phone, style: .expandable)) } if var fax = contact.fax { @@ -510,18 +512,18 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { fax += " \(faxExt)" } - rows.append(CopyCellRow(title: "Fax", content: fax)) + rows.append(.row(title: "Fax", content: fax, style: .expandable)) } - content.append(CopyCellView(title: "Billing Contact", rows: rows)) + content.append(.multiple(title: "Billing Contact", contents: rows)) } else if let contact = record.registryData.billingContact { - var rows = [CopyCellRow]() + var rows = [CopyCellType]() if let name = contact.name { - rows.append(CopyCellRow(title: "Name", content: name)) + rows.append(.row(title: "Name", content: name, style: .expandable)) } if let org = contact.organization { - rows.append(CopyCellRow(title: "Organization", content: org)) + rows.append(.row(title: "Organization", content: org, style: .expandable)) } var street = [String]() @@ -538,32 +540,32 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { street.append(address) } if !street.isEmpty { - let streetCell = CopyCellRow(title: "Street", content: street.joined(separator: "\n")) + let streetCell = CopyCellType.row(title: "Street", content: street.joined(separator: "\n"), style: .expandable) rows.append(streetCell) } if let city = contact.city { - rows.append(CopyCellRow(title: "City", content: city)) + rows.append(.row(title: "City", content: city, style: .expandable)) } if let postCode = contact.postalCode { - rows.append(CopyCellRow(title: "Postal Code", content: postCode)) + rows.append(.row(title: "Postal Code", content: postCode, style: .expandable)) } if let state = contact.state { - rows.append(CopyCellRow(title: "State", content: state)) + rows.append(.row(title: "State", content: state, style: .expandable)) } if let country = contact.country { if let countryCode = contact.countryCode { - rows.append(CopyCellRow(title: "Country", content: country + "(\(countryCode))")) + rows.append(.row(title: "Country", content: country + "(\(countryCode))", style: .expandable)) } else { - rows.append(CopyCellRow(title: "Country", content: country)) + rows.append(.row(title: "Country", content: country, style: .expandable)) } } if let email = contact.email { - rows.append(CopyCellRow(title: "Email", content: email)) + rows.append(.row(title: "Email", content: email, style: .expandable)) } if var phone = contact.telephone { @@ -571,7 +573,7 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { phone += " \(phoneExt)" } - rows.append(CopyCellRow(title: "Phone", content: phone)) + rows.append(.row(title: "Phone", content: phone, style: .expandable)) } if var fax = contact.fax { @@ -579,29 +581,31 @@ class WhoisXmlWhoisSectionModel: HostSectionModel { fax += " \(faxExt)" } - rows.append(CopyCellRow(title: "Fax", content: fax)) + rows.append(.row(title: "Fax", content: fax, style: .expandable)) } - content.append(CopyCellView(title: "Billing Contact", rows: rows)) + content.append(.multiple(title: "Billing Contact", contents: rows)) } - content.append(CopyCellView(title: "Availability", content: record.domainAvailability)) + if let domainAvailability = record.domainAvailability { + content.append(.row(title: "Availability", content: domainAvailability)) + } - let status = record.status ?? record.registryData.status - content.append(CopyCellView(title: "Status", content: status)) + let status = record.status ?? record.registryData.status ?? "Unknown" + content.append(.row(title: "Status", content: status)) if let customFieldName = record.customField1Name, let customFieldValue = record.customField1Value { - let customCell = CopyCellView(title: customFieldName, content: customFieldValue) + let customCell = CopyCellType.row(title: customFieldName, content: customFieldValue) content.append(customCell) } if let customFieldName = record.customField2Name, let customFieldValue = record.customField2Value { - let customCell = CopyCellView(title: customFieldName, content: customFieldValue) + let customCell = CopyCellType.row(title: customFieldName, content: customFieldValue) content.append(customCell) } if let customFieldName = record.customField3Name, let customFieldValue = record.customField3Value { - let customCell = CopyCellView(title: customFieldName, content: customFieldValue) + let customCell = CopyCellType.row(title: customFieldName, content: customFieldValue) content.append(customCell) } return copyData diff --git a/ec3730/Modifiers/PaddingListModifier.swift b/ec3730/Modifiers/PaddingListModifier.swift new file mode 100644 index 0000000..27389b2 --- /dev/null +++ b/ec3730/Modifiers/PaddingListModifier.swift @@ -0,0 +1,14 @@ +import SwiftUI + +struct PaddingListModifier: ViewModifier { + let padding: [(Edge.Set, CGFloat?)] + func body(content: Content) -> some View { + if let first = padding.first { + content + .padding(first.0, first.1) + .modifier(PaddingListModifier(padding: Array(padding.dropFirst()))) + } else { + content + } + } +} diff --git a/ec3730/Views/CopyCell/CopyCellChevronView.swift b/ec3730/Views/CopyCell/CopyCellChevronView.swift new file mode 100644 index 0000000..f3f68cd --- /dev/null +++ b/ec3730/Views/CopyCell/CopyCellChevronView.swift @@ -0,0 +1,17 @@ +import SwiftUI + +struct CopyCellChevronView: View { + var body: some View { + Image(systemName: "chevron.right") + .font(.system(size: 14, weight: .semibold)) + // .imageScale(.small) + .foregroundColor(Color(UIColor.systemGray3)) + /* + height 14 + color: + "0.9999999403953552", + "0.9999999403953552", + "0.9999999403953552" + */ + } +} diff --git a/ec3730/Views/CopyCell/CopyCellContentView.swift b/ec3730/Views/CopyCell/CopyCellContentView.swift new file mode 100644 index 0000000..a926bb2 --- /dev/null +++ b/ec3730/Views/CopyCell/CopyCellContentView.swift @@ -0,0 +1,17 @@ +import SwiftUI + +struct CopyCellContentView: View { + var content: String + let style: CopyCellStyleConfig + + var body: some View { + HStack(alignment: .center) { + Spacer() + Text(content).foregroundColor(style.detailStyle.color) + if style.chevron { + CopyCellChevronView() + } + } + .modifier(PaddingListModifier(padding: style.padding)) + } +} diff --git a/ec3730/Views/CopyCell/CopyCellDetailStyle.swift b/ec3730/Views/CopyCell/CopyCellDetailStyle.swift new file mode 100644 index 0000000..682dfbc --- /dev/null +++ b/ec3730/Views/CopyCell/CopyCellDetailStyle.swift @@ -0,0 +1,18 @@ +import SwiftUI + +enum CopyCellDetailStyle { + case gray + case accent + case label + + var color: Color { + switch self { + case .gray: + return .gray + case .accent: + return .accentColor + case .label: + return Color(UIColor.label) + } + } +} diff --git a/ec3730/Views/CopyCell/CopyCellMultipleTypesView.swift b/ec3730/Views/CopyCell/CopyCellMultipleTypesView.swift new file mode 100644 index 0000000..496b2c3 --- /dev/null +++ b/ec3730/Views/CopyCell/CopyCellMultipleTypesView.swift @@ -0,0 +1,19 @@ +import SwiftUI + +struct CopyCellMultipleTypesView: View { + var title: String + var contents: [CopyCellType] + + @Binding var expanded: Bool + + var body: some View { + DisclosureGroup(isExpanded: $expanded, content: { + ForEach(contents) { content in + content + } + }, label: { + Text(title) + }) + .padding() + } +} diff --git a/ec3730/Views/CopyCell/CopyCellSingleItemRowView.swift b/ec3730/Views/CopyCell/CopyCellSingleItemRowView.swift new file mode 100644 index 0000000..372c2e5 --- /dev/null +++ b/ec3730/Views/CopyCell/CopyCellSingleItemRowView.swift @@ -0,0 +1,19 @@ +import SwiftUI + +struct CopyCellSingleItemRowView: View { + var title: String + var content: String + let style: CopyCellStyleConfig + + var body: some View { + HStack(alignment: .center) { + Text(title) + Spacer() + Text(content).foregroundColor(style.detailStyle.color) + if style.chevron { + CopyCellChevronView() + } + } + .modifier(PaddingListModifier(padding: style.padding)) + } +} diff --git a/ec3730/Views/CopyCell/CopyCellStyleConfig.swift b/ec3730/Views/CopyCell/CopyCellStyleConfig.swift new file mode 100644 index 0000000..0caad3d --- /dev/null +++ b/ec3730/Views/CopyCell/CopyCellStyleConfig.swift @@ -0,0 +1,18 @@ +import SwiftUI + +struct CopyCellStyleConfig { + var detailStyle: CopyCellDetailStyle + var padding: [(Edge.Set, CGFloat?)] = [(Edge.Set.all, nil)] + var chevron: Bool = false + + static let gray: Self = .init(detailStyle: .gray) + // A Style for a cell in an multiple cell + static let expandable: Self = .init( + detailStyle: .label, + padding: [ + ([.leading, .trailing], nil), + (.top, 4), + ] + ) + static let chevron: Self = .init(detailStyle: .gray, chevron: true) +} diff --git a/ec3730/Views/CopyCell/CopyCellToggleableItemRowView.swift b/ec3730/Views/CopyCell/CopyCellToggleableItemRowView.swift new file mode 100644 index 0000000..9c1d314 --- /dev/null +++ b/ec3730/Views/CopyCell/CopyCellToggleableItemRowView.swift @@ -0,0 +1,21 @@ +import SwiftUI + +struct CopyCellToggleableItemRowView: View { + var title: String + var contents: [String] + let style: CopyCellStyleConfig + + var body: some View { + HStack(alignment: .center) { + Text(self.title) + Spacer() + TappedText(content: contents) + .foregroundColor(style.detailStyle.color) + + if style.chevron { + CopyCellChevronView() + } + } + .modifier(PaddingListModifier(padding: style.padding)) + } +} diff --git a/ec3730/Views/CopyCell/CopyCellType.swift b/ec3730/Views/CopyCell/CopyCellType.swift new file mode 100644 index 0000000..cfcfd81 --- /dev/null +++ b/ec3730/Views/CopyCell/CopyCellType.swift @@ -0,0 +1,119 @@ +import SwiftUI + +enum CopyCellType: NewCopyCellProtocol { + static func == (lhs: CopyCellType, rhs: CopyCellType) -> Bool { + lhs.id == rhs.id + } + + var json: [String: Any] { + switch self { + case let .toggleableRow(title: title, contents: contents, style: _): + return [title: contents] + case let .row(title: title, content: content, style: _): + return [title: content] + case let .multiple(title: title, contents: contents): + return [title: contents.map(\.json)] + case .content(let value, style: _): + return ["String": value] + case .custom(shareable: let shareable, content: _): + let key = "\(type(of: shareable))" + if let dict = shareable.dictionary { + return dict + } else if shareable is String { + return [key: shareable] + } else if let data = try? NSKeyedArchiver.archivedData(withRootObject: shareable, requiringSecureCoding: false) { + return [key: data.base64EncodedString()] + } + return [:] + } + } + + var shareable: any Shareable { + let dict = self.json + guard JSONSerialization.isValidJSONObject(dict), let data = try? JSONSerialization.data(withJSONObject: dict), let string = String(data: data, encoding: .utf8) else { + return "{}" + } + return string + } + + var isCopyable: Bool { + switch shareable { + case is String, is [String]: + return true + case is UIImage, is [UIImage]: + return true + case is UIColor, is [UIColor]: + return true + case is URL, is [URL]: + return true + default: + return false + } + } + + var isExpandable: Bool { + switch self { + case .multiple(title: _, contents: _): + return true + default: + return false + } + } + + var id: Int { + shareable.hashValue + } + + func hash(into hasher: inout Hasher) { + hasher.combine(shareable) + } + + /// A style for a cell with the label that is right aligned + case content(_ value: String, style: CopyCellStyleConfig) + /// A style for a cell with a label on the left side of the cell with left-aligned and black text; on the right side is a label that has gray text and is right-aligned. + case row(title: String, content: String, style: CopyCellStyleConfig = .gray) + case toggleableRow(title: String, contents: [String], style: CopyCellStyleConfig = .gray) + case multiple(title: String, contents: [Self]) + case custom(shareable: any Shareable, content: AnyView) + + var body: some View { + CopyCellTypeView(type: self) + } +} + +@available(iOS 15.0, *) +struct CopyCellType_Previews: PreviewProvider { + static var previews: some View { + Group { + VStack { + CopyCellType.row(title: "Hello", content: "World") + CopyCellType.row(title: "Hello", content: "World", style: .chevron) + CopyCellType.custom(shareable: "Boo!", content: AnyView(Text("👻"))) + CopyCellType.custom(shareable: UIImage(systemName: "gear"), content: AnyView(Image(systemName: "gear"))) + } + + VStack { + CopyCellType.toggleableRow(title: "Title", contents: ["Content 1", "Content 2", "Content 3"]) + CopyCellType.toggleableRow(title: "Title", contents: ["Content 1", "Content 2", "Content 3"], style: .chevron) + } + + VStack { + CopyCellType.multiple(title: "Title", contents: [ + CopyCellType.row(title: "Hello", content: "World"), + CopyCellType.row(title: "Title", content: "Content 2"), + ]) + CopyCellType.multiple(title: "Title", contents: [ + CopyCellType.row(title: "Hello", content: "World"), + CopyCellType.row(title: "Hello", content: "World"), + CopyCellType.toggleableRow(title: "Title", contents: ["Content 1", "Content 2", "Content 3"]), + ]) + CopyCellType.multiple(title: "Title", contents: [ + CopyCellType.row(title: "Hello", content: "World", style: .expandable), + CopyCellType.row(title: "Hello", content: "World", style: .expandable), + CopyCellType.toggleableRow(title: "Title", contents: ["Content 1", "Content 2", "Content 3"], style: .expandable), + ]) + } + } + .previewLayout(.sizeThatFits) + } +} diff --git a/ec3730/Views/CopyCell/CopyCellTypeView.swift b/ec3730/Views/CopyCell/CopyCellTypeView.swift new file mode 100644 index 0000000..248f743 --- /dev/null +++ b/ec3730/Views/CopyCell/CopyCellTypeView.swift @@ -0,0 +1,73 @@ +import SwiftUI + +struct CopyCellTypeView: View { + var type: CopyCellType + var backgroundColor = Color(UIColor.systemBackground) + @State private var shouldShare: Bool = false + @State var expanded: Bool = true + + @ViewBuilder + var typeView: some View { + switch type { + case let .row(title: title, content: content, style: style): + CopyCellSingleItemRowView(title: title, content: content, style: style) + case let .toggleableRow(title: title, contents: contents, style: style): + CopyCellToggleableItemRowView(title: title, contents: contents, style: style) + case let .multiple(title: title, contents: content): + CopyCellMultipleTypesView(title: title, contents: content, expanded: $expanded) + case let .custom(shareable: _, content: content): + content + case let .content(value, style: style): + CopyCellContentView(content: value, style: style) + } + } + + var body: some View { + typeView + .background(backgroundColor) + .contextMenu(menuItems: { + if type.isCopyable { + Button(action: { + switch type.shareable { + case is String: + UIPasteboard.general.string = type.shareable as? String + case is [String]: + UIPasteboard.general.strings = type.shareable as? [String] + case is UIImage: + UIPasteboard.general.image = type.shareable as? UIImage + case is [UIImage]: + UIPasteboard.general.images = type.shareable as? [UIImage] + case is UIColor: + UIPasteboard.general.color = type.shareable as? UIColor + case is [UIColor]: + UIPasteboard.general.colors = type.shareable as? [UIColor] + case is URL: + UIPasteboard.general.url = type.shareable as? URL + case is [URL]: + UIPasteboard.general.urls = type.shareable as? [URL] + default: + break + } + }, label: { + Label("Copy", systemImage: "doc.on.doc") + }) + } + Button(action: { + self.shouldShare.toggle() + + }, label: { + Label("Share", systemImage: "square.and.arrow.up") + }) + + if type.isExpandable { + Button { + expanded.toggle() + } label: { + Label(expanded ? "Collapse" : "Expand", systemImage: expanded ? "rectangle.compress.vertical" : "rectangle.expand.vertical") + } + } + }).sheet(isPresented: $shouldShare, content: { + ShareSheetView(activityItems: [type.shareable]) + }) + } +} diff --git a/ec3730/Views/CopyCell/NewCopyCellProtocol.swift b/ec3730/Views/CopyCell/NewCopyCellProtocol.swift new file mode 100644 index 0000000..15de914 --- /dev/null +++ b/ec3730/Views/CopyCell/NewCopyCellProtocol.swift @@ -0,0 +1,6 @@ +import SwiftUI + +protocol NewCopyCellProtocol: View, Hashable, Identifiable { + typealias Shareable = Codable & Hashable + var shareable: any Shareable { get } +} diff --git a/ec3730/Views/CopyCellView.swift b/ec3730/Views/CopyCellView.swift deleted file mode 100644 index 16f243b..0000000 --- a/ec3730/Views/CopyCellView.swift +++ /dev/null @@ -1,176 +0,0 @@ -import SwiftUI - -protocol CopyCellProtocol: View, Hashable, Identifiable { - var contentsToShare: String { get } -} - -struct CopyCellRow: Identifiable, Hashable, Codable { - var id: Int { - hashValue - } - - var title: String? - var content: String? - var contents: [String]? -} - -@available(iOS 15.0, *) -struct CopyCellView: CopyCellProtocol { - var id: String { - contentsToShare + "\(hashValue)" - } - - static func == (lhs: CopyCellView, rhs: CopyCellView) -> Bool { - lhs.title == rhs.title && lhs.content == rhs.content - } - - func hash(into hasher: inout Hasher) { - hasher.combine(title) - hasher.combine(content) - } - - var title: String - var content: String? - var contents: [String]? - var rows: [CopyCellRow]? - var backgroundColor = Color(UIColor.systemBackground) - var withChevron = false - - @State var shouldShare: Bool = false - - var contentsToShare: String { - if let content = content { - let dict = [title: content] - guard let data = try? JSONSerialization.data(withJSONObject: dict), let string = String(data: data, encoding: .utf8) else { - return "{}" - } - - return string - } - - if let rows = rows { - let dict = [title: rows] - guard let data = try? JSONEncoder().encode(dict), let string = String(data: data, encoding: .utf8) else { - return "{}" - } - - return string - } - - return "{}" - } - - @State var expanded = true - - var body: some View { - VStack(alignment: .leading, spacing: 0) { - if let content = self.content { - HStack(alignment: .center) { - Text(self.title) - Spacer() - Text(content).foregroundColor(.gray) - if withChevron { - Image(systemName: "chevron.right") - .font(.system(size: 14, weight: .semibold)) - // .imageScale(.small) - .foregroundColor(Color(UIColor.systemGray3)) - /* - height 14 - color: - "0.9999999403953552", - "0.9999999403953552", - "0.9999999403953552" - */ - } - }.padding() - } else if let rows = self.rows { - DisclosureGroup(isExpanded: $expanded, content: { - ForEach(rows, id: \.self) { row in - HStack(alignment: .center) { - if let title = row.title { - Text(title) - } - Spacer() - if let content = row.content { - Text(content) - } - if let contents = row.contents { - TappedText(content: contents) - } - }.padding([.leading, .trailing]).padding(.top, 4) - } - }, label: { - Text(self.title) - }).padding() - } else if let contents = contents { - HStack(alignment: .center) { - Text(self.title) - Spacer() - TappedText(content: contents) - .foregroundColor(.gray) - - if withChevron { - Image(systemName: "chevron.right") - .font(.system(size: 14, weight: .semibold)) - // .imageScale(.small) - .foregroundColor(Color(UIColor.systemGray3)) - /* - height 14 - color: - "0.9999999403953552", - "0.9999999403953552", - "0.9999999403953552" - */ - } - }.padding() - } - } - .background(backgroundColor) - .contextMenu(menuItems: { - Button(action: { - UIPasteboard.general.string = content - }, label: { - Label("Copy", systemImage: "doc.on.doc") - }) - Button(action: { self.shouldShare.toggle() }, label: { - Label("Share", systemImage: "square.and.arrow.up") - }) - if let _ = self.rows { - Button { - expanded.toggle() - } label: { - Label(expanded ? "Collapse" : "Expand", systemImage: expanded ? "rectangle.compress.vertical" : "rectangle.expand.vertical") - } - } - }).sheet(isPresented: $shouldShare, content: { - ShareSheetView(activityItems: [self.contentsToShare]) - }) - } -} - -struct TappedText: View { - @State private var selectedTextIndex: Int = 0 - - var content: [String] - - var body: some View { - Text(content[selectedTextIndex]) - .onTapGesture { - let temp = selectedTextIndex + 1 - - selectedTextIndex = temp >= content.count ? 0 : temp - } - } -} - -@available(iOS 15.0, *) -struct CopyCellView_Previews: PreviewProvider { - static var previews: some View { - Group { - CopyCellView(title: "Title", content: "Detail") - CopyCellView(title: "Test", rows: [CopyCellRow(title: "", content: "whatever")]) - CopyCellView(title: "Test", rows: [CopyCellRow(title: "", content: "whatever"), CopyCellRow(title: "", content: "whatever"), CopyCellRow(title: "", content: "whatever2"), CopyCellRow(title: "", content: "whatever3")]) - CopyCellView(title: "Test", rows: [CopyCellRow(title: "t1", content: "whatever"), CopyCellRow(title: "t2", content: "whatever2")]) - }.previewLayout(.sizeThatFits) - } -} diff --git a/ec3730/Views/Interface/InterfaceListView.swift b/ec3730/Views/Interface/InterfaceListView.swift index f5a24e4..4aec906 100644 --- a/ec3730/Views/Interface/InterfaceListView.swift +++ b/ec3730/Views/Interface/InterfaceListView.swift @@ -70,7 +70,7 @@ struct InterfaceListView: View { NavigationLink(destination: { InterfaceView(model: model, interface: interface) }, label: { - CopyCellView(title: interface.name, content: interface.address, withChevron: true) + CopyCellType.row(title: interface.name, content: interface.address ?? "-", style: .chevron) }) } } diff --git a/ec3730/Views/Interface/InterfaceView.swift b/ec3730/Views/Interface/InterfaceView.swift index 962f297..8846457 100644 --- a/ec3730/Views/Interface/InterfaceView.swift +++ b/ec3730/Views/Interface/InterfaceView.swift @@ -43,43 +43,43 @@ struct InterfaceView: View { @ViewBuilder private func proxyView(_ item: (key: String, title: String), info proxyInfo: [String: Any?]) -> some View { if let value = proxyInfo[item.key] as? String { - CopyCellView(title: item.title, content: value) + CopyCellType.row(title: item.title, content: value) } else if let values = proxyInfo[item.key] as? [String] { - CopyCellView(title: item.title, rows: values.map { CopyCellRow(content: $0) }) + CopyCellType.multiple(title: item.title, contents: values.map { .content($0, style: .expandable) }) } else if let value = proxyInfo[item.key] as? Int { - CopyCellView(title: item.title, content: String(describing: value)) + CopyCellType.row(title: item.title, content: String(describing: value)) } else if let value = proxyInfo[item.key] as? Bool { - CopyCellView(title: item.title, content: value ? "Yes" : "No") + CopyCellType.row(title: item.title, content: value ? "Yes" : "No") } else if let value = proxyInfo[item.key] { - CopyCellView(title: item.key, content: String(describing: value)) + CopyCellType.row(title: item.key, content: String(describing: value)) } } @ViewBuilder func basicInfo() -> some View { - CopyCellView(title: "Name", content: interface.name) + CopyCellType.row(title: "Name", content: interface.name) if let address = interface.address { - CopyCellView(title: "Address", content: address) + CopyCellType.row(title: "Address", content: address) } if let netmask = interface.netmask { - CopyCellView(title: "Netmask", content: netmask) + CopyCellType.row(title: "Netmask", content: netmask) } if let broadcastAddress = interface.broadcastAddress { - CopyCellView(title: "Broadcast Address", content: broadcastAddress) + CopyCellType.row(title: "Broadcast Address", content: broadcastAddress) } - CopyCellView(title: "Family", content: interface.family.toString()) - CopyCellView(title: "Loopback", content: interface.isLoopback ? "Yes" : "No") - CopyCellView(title: "Runing", content: interface.isRunning ? "Yes" : "No") - CopyCellView(title: "Up", content: interface.isUp ? "Yes" : "No") - CopyCellView(title: "Supports Multicast", content: interface.supportsMulticast ? "Yes" : "No") + CopyCellType.row(title: "Family", content: interface.family.toString()) + CopyCellType.row(title: "Loopback", content: interface.isLoopback ? "Yes" : "No") + CopyCellType.row(title: "Runing", content: interface.isRunning ? "Yes" : "No") + CopyCellType.row(title: "Up", content: interface.isUp ? "Yes" : "No") + CopyCellType.row(title: "Supports Multicast", content: interface.supportsMulticast ? "Yes" : "No") } @ViewBuilder func wifiInfo() -> some View { if isCurrentWifiInterface, let interfaceInfo = model.wifiInterfaceInfo { if let SSID = interfaceInfo[kCNNetworkInfoKeySSID as String] as? String { - CopyCellView(title: "SSID", content: SSID) + CopyCellType.row(title: "SSID", content: SSID) } if let BSSID = interfaceInfo[kCNNetworkInfoKeyBSSID as String] as? String { - CopyCellView(title: "BSSID", content: BSSID) + CopyCellType.row(title: "BSSID", content: BSSID) } } } diff --git a/ec3730/Views/TappedText.swift b/ec3730/Views/TappedText.swift new file mode 100644 index 0000000..fefa550 --- /dev/null +++ b/ec3730/Views/TappedText.swift @@ -0,0 +1,24 @@ +import SwiftUI + +struct TappedText: View { + @State private var selectedTextIndex: Int = 0 + + var content: [String] + + var body: some View { + Text(content[selectedTextIndex]) + .onTapGesture { + let temp = selectedTextIndex + 1 + + selectedTextIndex = temp >= content.count ? 0 : temp + } + } +} + +#if DEBUG + struct TappedTextPreview: PreviewProvider { + static var previews: some View { + TappedText(content: (1 ... 5).map { "Content \($0)" }) + } + } +#endif