From ede15c586af71b8d6f2c190ffdad0324414cac06 Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Fri, 3 Apr 2026 16:17:43 -0500 Subject: fix: various bugs - fix Email section to left align - ensure container contents can overflow scroll but page cannot - fix map width --- DomainDig/ContentView.swift | 77 ++++++++++++++++++++++++++------------------- DomainDig/HistoryView.swift | 77 ++++++++++++++++++++++++++------------------- 2 files changed, 90 insertions(+), 64 deletions(-) (limited to 'DomainDig') diff --git a/DomainDig/ContentView.swift b/DomainDig/ContentView.swift index 940a25b..b6a7607 100644 --- a/DomainDig/ContentView.swift +++ b/DomainDig/ContentView.swift @@ -7,7 +7,7 @@ struct ContentView: View { var body: some View { NavigationStack { - ScrollView { + ScrollView(.vertical) { VStack(spacing: 0) { inputSection if viewModel.hasRun { @@ -225,7 +225,7 @@ struct ContentView: View { .background(Color(.systemGray6).opacity(0.5)) .cornerRadius(6) } else { - VStack(alignment: .leading, spacing: 4) { + horizontallyScrollableCard { ForEach(viewModel.redirectChain) { hop in HStack(alignment: .top, spacing: 6) { Text("\(hop.stepNumber)") @@ -248,9 +248,6 @@ struct ContentView: View { } } } - .padding(10) - .background(Color(.systemGray6).opacity(0.5)) - .cornerRadius(6) } } .padding(.top, 16) @@ -281,7 +278,7 @@ struct ContentView: View { } private var ptrRow: some View { - VStack(alignment: .leading, spacing: 4) { + horizontallyScrollableCard { Text("PTR (Reverse DNS)") .font(.system(.subheadline, design: .monospaced)) .fontWeight(.semibold) @@ -302,13 +299,10 @@ struct ContentView: View { .foregroundStyle(.secondary) } } - .padding(10) - .background(Color(.systemGray6).opacity(0.5)) - .cornerRadius(6) } private func dnsRecordSection(_ section: DNSSection) -> some View { - VStack(alignment: .leading, spacing: 4) { + horizontallyScrollableCard { Text(section.recordType.rawValue) .font(.system(.subheadline, design: .monospaced)) .fontWeight(.semibold) @@ -334,9 +328,6 @@ struct ContentView: View { dnsRecordRows(section.wildcardRecords) } } - .padding(10) - .background(Color(.systemGray6).opacity(0.5)) - .cornerRadius(6) } private func dnsRecordRows(_ records: [DNSRecord]) -> some View { @@ -369,16 +360,14 @@ struct ContentView: View { } else if let error = viewModel.emailSecurityError { errorLabel(error) } else if let email = viewModel.emailSecurity { - VStack(alignment: .leading, spacing: 6) { + horizontallyScrollableCard(spacing: 6) { emailSecurityRow("SPF", record: email.spf) emailSecurityRow("DMARC", record: email.dmarc) emailSecurityRow("DKIM", record: email.dkim) } - .padding(10) - .background(Color(.systemGray6).opacity(0.5)) - .cornerRadius(6) } } + .frame(maxWidth: .infinity, alignment: .leading) .padding(.top, 16) } @@ -434,7 +423,7 @@ struct ContentView: View { } private func sslDetail(_ info: SSLCertificateInfo) -> some View { - VStack(alignment: .leading, spacing: 8) { + horizontallyScrollableCard(spacing: 8) { certRow("Common Name", info.commonName) certRow("Issuer", info.issuer) @@ -466,9 +455,6 @@ struct ContentView: View { certRow("Chain Depth", "\(info.chainDepth)") } - .padding(10) - .background(Color(.systemGray6).opacity(0.5)) - .cornerRadius(6) } // MARK: - HTTP Headers @@ -484,7 +470,7 @@ struct ContentView: View { } else if let error = viewModel.httpHeadersError { errorLabel(error) } else { - VStack(alignment: .leading, spacing: 4) { + horizontallyScrollableCard { ForEach(viewModel.httpHeaders) { header in HStack(alignment: .top, spacing: 4) { Text(header.name + ":") @@ -497,9 +483,6 @@ struct ContentView: View { } } } - .padding(10) - .background(Color(.systemGray6).opacity(0.5)) - .cornerRadius(6) } } .padding(.top, 16) @@ -533,14 +516,17 @@ struct ContentView: View { private func ipGeolocationDetail(_ geo: IPGeolocation) -> some View { VStack(alignment: .leading, spacing: 6) { - certRow("IP", geo.ip) - if let org = geo.org { - certRow("Org / ISP", org) - } - let location = [geo.city, geo.region, geo.country_name].compactMap { $0 }.joined(separator: ", ") - if !location.isEmpty { - certRow("Location", location) + horizontallyScrollableContent(spacing: 6) { + certRow("IP", geo.ip) + if let org = geo.org { + certRow("Org / ISP", org) + } + let location = [geo.city, geo.region, geo.country_name].compactMap { $0 }.joined(separator: ", ") + if !location.isEmpty { + certRow("Location", location) + } } + if let lat = geo.latitude, let lon = geo.longitude { let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon) Map(initialPosition: .region(MKCoordinateRegion( @@ -550,6 +536,7 @@ struct ContentView: View { Marker(geo.ip, coordinate: coordinate) } .mapStyle(.standard) + .frame(maxWidth: .infinity) .frame(height: 180) .cornerRadius(8) } @@ -620,6 +607,32 @@ struct ContentView: View { } } + private func horizontallyScrollableCard( + spacing: CGFloat = 4, + @ViewBuilder content: () -> Content + ) -> some View { + horizontallyScrollableContent(spacing: spacing) { + content() + } + .padding(10) + .background(Color(.systemGray6).opacity(0.5)) + .cornerRadius(6) + } + + private func horizontallyScrollableContent( + spacing: CGFloat = 4, + @ViewBuilder content: () -> Content + ) -> some View { + ScrollView(.horizontal) { + VStack(alignment: .leading, spacing: spacing) { + content() + } + .scrollTargetLayout() + } + .scrollBounceBehavior(.basedOnSize, axes: .horizontal) + .frame(maxWidth: .infinity, alignment: .leading) + } + private func errorLabel(_ message: String) -> some View { Label(message, systemImage: "exclamationmark.triangle.fill") .font(.system(.caption, design: .monospaced)) diff --git a/DomainDig/HistoryView.swift b/DomainDig/HistoryView.swift index b64ebf3..1963509 100644 --- a/DomainDig/HistoryView.swift +++ b/DomainDig/HistoryView.swift @@ -65,7 +65,7 @@ struct HistoryDetailView: View { }() var body: some View { - ScrollView { + ScrollView(.vertical) { VStack(alignment: .leading, spacing: 0) { cachedBanner reachabilitySection @@ -157,7 +157,7 @@ struct HistoryDetailView: View { .background(Color(.systemGray6).opacity(0.5)) .cornerRadius(6) } else { - VStack(alignment: .leading, spacing: 4) { + horizontallyScrollableCard { ForEach(entry.redirectChain) { hop in HStack(alignment: .top, spacing: 6) { Text("\(hop.stepNumber)") @@ -180,9 +180,6 @@ struct HistoryDetailView: View { } } } - .padding(10) - .background(Color(.systemGray6).opacity(0.5)) - .cornerRadius(6) } } } @@ -195,7 +192,7 @@ struct HistoryDetailView: View { VStack(alignment: .leading, spacing: 12) { sectionHeader("DNS Records") ForEach(entry.dnsSections) { section in - VStack(alignment: .leading, spacing: 4) { + horizontallyScrollableCard { Text(section.recordType.rawValue) .font(.system(.subheadline, design: .monospaced)) .fontWeight(.semibold) @@ -220,12 +217,9 @@ struct HistoryDetailView: View { recordRows(section.wildcardRecords) } } - .padding(10) - .background(Color(.systemGray6).opacity(0.5)) - .cornerRadius(6) if section.recordType == .A { - VStack(alignment: .leading, spacing: 4) { + horizontallyScrollableCard { Text("PTR (Reverse DNS)") .font(.system(.subheadline, design: .monospaced)) .fontWeight(.semibold) @@ -242,9 +236,6 @@ struct HistoryDetailView: View { .foregroundStyle(.secondary) } } - .padding(10) - .background(Color(.systemGray6).opacity(0.5)) - .cornerRadius(6) } } } @@ -259,16 +250,14 @@ struct HistoryDetailView: View { VStack(alignment: .leading, spacing: 12) { if let email = entry.emailSecurity { sectionHeader("Email Security") - VStack(alignment: .leading, spacing: 6) { + horizontallyScrollableCard(spacing: 6) { historyEmailRow("SPF", record: email.spf) historyEmailRow("DMARC", record: email.dmarc) historyEmailRow("DKIM", record: email.dkim) } - .padding(10) - .background(Color(.systemGray6).opacity(0.5)) - .cornerRadius(6) } } + .frame(maxWidth: .infinity, alignment: .leading) .padding(.top, 16) } @@ -310,7 +299,7 @@ struct HistoryDetailView: View { VStack(alignment: .leading, spacing: 12) { if let info = entry.sslInfo { sectionHeader("SSL / TLS Certificate") - VStack(alignment: .leading, spacing: 8) { + horizontallyScrollableCard(spacing: 8) { labelRow("Common Name", info.commonName) labelRow("Issuer", info.issuer) @@ -341,9 +330,6 @@ struct HistoryDetailView: View { labelRow("Chain Depth", "\(info.chainDepth)") } - .padding(10) - .background(Color(.systemGray6).opacity(0.5)) - .cornerRadius(6) } } .padding(.top, 16) @@ -355,7 +341,7 @@ struct HistoryDetailView: View { VStack(alignment: .leading, spacing: 12) { if !entry.httpHeaders.isEmpty { sectionHeader("HTTP Headers") - VStack(alignment: .leading, spacing: 4) { + horizontallyScrollableCard { ForEach(entry.httpHeaders) { header in HStack(alignment: .top, spacing: 4) { Text(header.name + ":") @@ -368,9 +354,6 @@ struct HistoryDetailView: View { } } } - .padding(10) - .background(Color(.systemGray6).opacity(0.5)) - .cornerRadius(6) } } .padding(.top, 16) @@ -383,14 +366,17 @@ struct HistoryDetailView: View { if let geo = entry.ipGeolocation { sectionHeader("IP Location") VStack(alignment: .leading, spacing: 6) { - labelRow("IP", geo.ip) - if let org = geo.org { - labelRow("Org / ISP", org) - } - let location = [geo.city, geo.region, geo.country_name].compactMap { $0 }.joined(separator: ", ") - if !location.isEmpty { - labelRow("Location", location) + horizontallyScrollableContent(spacing: 6) { + labelRow("IP", geo.ip) + if let org = geo.org { + labelRow("Org / ISP", org) + } + let location = [geo.city, geo.region, geo.country_name].compactMap { $0 }.joined(separator: ", ") + if !location.isEmpty { + labelRow("Location", location) + } } + if let lat = geo.latitude, let lon = geo.longitude { let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon) Map(initialPosition: .region(MKCoordinateRegion( @@ -400,6 +386,7 @@ struct HistoryDetailView: View { Marker(geo.ip, coordinate: coordinate) } .mapStyle(.standard) + .frame(maxWidth: .infinity) .frame(height: 180) .cornerRadius(8) } @@ -481,6 +468,32 @@ struct HistoryDetailView: View { } } + private func horizontallyScrollableCard( + spacing: CGFloat = 4, + @ViewBuilder content: () -> Content + ) -> some View { + horizontallyScrollableContent(spacing: spacing) { + content() + } + .padding(10) + .background(Color(.systemGray6).opacity(0.5)) + .cornerRadius(6) + } + + private func horizontallyScrollableContent( + spacing: CGFloat = 4, + @ViewBuilder content: () -> Content + ) -> some View { + ScrollView(.horizontal) { + VStack(alignment: .leading, spacing: spacing) { + content() + } + .scrollTargetLayout() + } + .scrollBounceBehavior(.basedOnSize, axes: .horizontal) + .frame(maxWidth: .infinity, alignment: .leading) + } + private func errorLabel(_ message: String) -> some View { Label(message, systemImage: "exclamationmark.triangle.fill") .font(.system(.caption, design: .monospaced)) -- cgit v1.2.3