feat: initial ui implementation
This commit is contained in:
parent
1612ed099c
commit
13254d63c2
9 changed files with 709 additions and 31 deletions
281
dns/HomeView.swift
Normal file
281
dns/HomeView.swift
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
enum BlocklistOption: String, CaseIterable, Identifiable {
|
||||
case secure = "Secure"
|
||||
case securePlusAdblock = "Secure + Adblock"
|
||||
|
||||
var id: String { rawValue }
|
||||
|
||||
var enabled: Bool {
|
||||
switch self {
|
||||
case .secure:
|
||||
return true
|
||||
case .securePlusAdblock:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .secure:
|
||||
return "Malware and phishing protection"
|
||||
case .securePlusAdblock:
|
||||
return "Security plus ad and tracker blocking"
|
||||
}
|
||||
}
|
||||
|
||||
var icon: String {
|
||||
switch self {
|
||||
case .secure:
|
||||
return "shield"
|
||||
case .securePlusAdblock:
|
||||
return "shield.righthalf.filled"
|
||||
}
|
||||
}
|
||||
|
||||
var server: String {
|
||||
switch self {
|
||||
case .secure:
|
||||
return "dns.sr2.uk"
|
||||
case .securePlusAdblock:
|
||||
return "dnsplus.sr2.uk"
|
||||
}
|
||||
}
|
||||
|
||||
var ipv4: String {
|
||||
switch self {
|
||||
case .secure:
|
||||
return "144.76.160.194"
|
||||
case .securePlusAdblock:
|
||||
return "192.0.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
var ipv6: String {
|
||||
switch self {
|
||||
case .secure:
|
||||
return "2a01:4f8:2210:23ea::4"
|
||||
case .securePlusAdblock:
|
||||
return "2001:db8::1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ServiceStatus {
|
||||
case pending
|
||||
case operational
|
||||
case degraded
|
||||
case outage
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .pending:
|
||||
return "Fetching service status"
|
||||
case .operational:
|
||||
return "No issues detected"
|
||||
case .degraded:
|
||||
return "Performance degraded"
|
||||
case .outage:
|
||||
return "Service disruption"
|
||||
}
|
||||
}
|
||||
|
||||
var color: Color {
|
||||
switch self {
|
||||
case .pending:
|
||||
return .gray
|
||||
case .operational:
|
||||
return .green
|
||||
case .degraded:
|
||||
return .orange
|
||||
case .outage:
|
||||
return .red
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct HomeView: View {
|
||||
@State private var isEnabled = false
|
||||
@State private var selectedBlocklist: BlocklistOption = .secure
|
||||
@State private var serviceStatus: ServiceStatus = .operational
|
||||
|
||||
private let falsePositiveURL = URL(string: "https://www.sr2.uk/contact")!
|
||||
private let statusURL = URL(string:
|
||||
"https://status.sr2.uk/")!
|
||||
private let tosURL = URL(string: "https://www.sr2.uk/terms")!
|
||||
private let privacyPolicyURL = URL(string: "https://www.sr2.uk/privacy")!
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
List {
|
||||
|
||||
// Main toggle section
|
||||
Section {
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("DNS Protection")
|
||||
.font(.headline)
|
||||
Text(isEnabled ? "Active" : "Inactive")
|
||||
.font(.caption)
|
||||
.foregroundStyle(isEnabled ? .green : .secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Toggle("", isOn: $isEnabled)
|
||||
.labelsHidden()
|
||||
.tint(.green)
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
|
||||
// Blocklist selection
|
||||
Section {
|
||||
ForEach(BlocklistOption.allCases) { option in
|
||||
BlocklistRow(
|
||||
option: option,
|
||||
isSelected: selectedBlocklist == option
|
||||
)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
guard option.enabled else { return }
|
||||
withAnimation(.spring(duration: 0.3)) {
|
||||
selectedBlocklist = option
|
||||
}
|
||||
}
|
||||
.opacity(option.enabled ? 1 : 0.6)
|
||||
}
|
||||
} header: {
|
||||
Text("Blocklist")
|
||||
} footer: {
|
||||
Text("Select the level of protection for your DNS queries")
|
||||
}
|
||||
|
||||
// Status section
|
||||
if isEnabled {
|
||||
Section {
|
||||
HStack {
|
||||
Label("Status", systemImage: "checkmark.circle.fill")
|
||||
.foregroundStyle(.green)
|
||||
Spacer()
|
||||
Text("Connected")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
HStack {
|
||||
Label("Server", systemImage: "server.rack")
|
||||
Spacer()
|
||||
Text(selectedBlocklist.server)
|
||||
.foregroundStyle(.secondary)
|
||||
.font(.system(.body, design: .monospaced))
|
||||
}
|
||||
|
||||
HStack {
|
||||
Label("IPv4", systemImage: "globe")
|
||||
Spacer()
|
||||
Text(selectedBlocklist.ipv4)
|
||||
.foregroundStyle(.secondary)
|
||||
.font(.system(.body, design: .monospaced))
|
||||
}
|
||||
|
||||
HStack {
|
||||
Label("IPv6", systemImage: "globe")
|
||||
Spacer()
|
||||
Text(selectedBlocklist.ipv6)
|
||||
.foregroundStyle(.secondary)
|
||||
.font(.system(.body, design: .monospaced))
|
||||
}
|
||||
|
||||
HStack {
|
||||
Label("Domains in blocklist", systemImage: "xmark.shield.fill")
|
||||
Spacer()
|
||||
BlockedCount()
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
} header: {
|
||||
Text("Connection Details")
|
||||
}
|
||||
}
|
||||
|
||||
// Support section
|
||||
Section {
|
||||
Link(destination: falsePositiveURL) {
|
||||
HStack {
|
||||
Label {
|
||||
Text("Report False Positive")
|
||||
} icon: {
|
||||
Image(systemName: "exclamationmark.bubble")
|
||||
.foregroundStyle(.orange)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(systemName: "arrow.up.right.square")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}.foregroundStyle(.primary)
|
||||
} footer: {
|
||||
Text("Submit incorrectly blocked domains for review")
|
||||
}
|
||||
|
||||
// Service status section
|
||||
Section {
|
||||
Link(destination: statusURL) {
|
||||
HStack(spacing: 12) {
|
||||
Circle()
|
||||
.fill(serviceStatus.color)
|
||||
.frame(width: 12, height: 12)
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Service Status")
|
||||
.font(.headline)
|
||||
Text(serviceStatus.description)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(systemName: "arrow.up.right.square")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
}.foregroundStyle(.primary)
|
||||
|
||||
Link(destination: tosURL) {
|
||||
HStack(spacing: 12) {
|
||||
Image(systemName: "doc.text")
|
||||
Text("Terms of Service")
|
||||
Spacer()
|
||||
Image(systemName: "arrow.up.right.square")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
}
|
||||
}.foregroundStyle(.primary)
|
||||
|
||||
Link(destination: privacyPolicyURL) {
|
||||
HStack(spacing: 12) {
|
||||
Image(systemName: "doc.text")
|
||||
Text("Privacy Policy")
|
||||
Spacer()
|
||||
Image(systemName: "arrow.up.right.square")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
}
|
||||
}.foregroundStyle(.primary)
|
||||
}
|
||||
}
|
||||
.navigationTitle("SR2® Cloud DNS")
|
||||
.animation(.default, value: isEnabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
HomeView()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue