krz/domain-dig
an ios app for DNS & SSL analysis
clone: git clone https://gitbay.org/krz/domain-dig.git
v4.5.0: DomainDig/RootTabView.swift · raw
1import SwiftUI
2
3private enum RootTab: Hashable {
4 case dashboard
5 case audit
6 case history
7 case inspect
8 case settings
9}
10
11struct RootTabView: View {
12 @Bindable var viewModel: DomainViewModel
13 @State private var purchaseService = PurchaseService.shared
14 @State private var intentRouter = DomainDigIntentRouter.shared
15 @State private var detailDomain: TrackedDomain?
16 @State private var selectedTab: RootTab = FeatureAccessService.currentTier == .free ? .inspect : .dashboard
17
18 var body: some View {
19 let _ = purchaseService.currentTier
20
21 TabView(selection: $selectedTab) {
22 NavigationStack {
23 DashboardView(viewModel: viewModel)
24 }
25 .tabItem {
26 Label("Dashboard", systemImage: "square.grid.2x2")
27 }
28 .tag(RootTab.dashboard)
29
30 NavigationStack {
31 AuditListView(viewModel: viewModel)
32 }
33 .tabItem {
34 Label("Audit", systemImage: "checklist")
35 }
36 .tag(RootTab.audit)
37
38 NavigationStack {
39 HistoryView(viewModel: viewModel)
40 }
41 .tabItem {
42 Label("History", systemImage: "clock.arrow.trianglehead.counterclockwise.rotate.90")
43 }
44 .tag(RootTab.history)
45
46 ContentView(viewModel: viewModel)
47 .tabItem {
48 Label("Inspect", systemImage: "magnifyingglass")
49 }
50 .tag(RootTab.inspect)
51
52 NavigationStack {
53 SettingsView(viewModel: viewModel)
54 }
55 .tabItem {
56 Label("Settings", systemImage: "gearshape")
57 }
58 .tag(RootTab.settings)
59 }
60 .sheet(isPresented: Binding(
61 get: { viewModel.isPaywallPresented },
62 set: { viewModel.isPaywallPresented = $0 }
63 )) {
64 PaywallView()
65 }
66 .alert(item: Binding(
67 get: { viewModel.upgradePrompt },
68 set: { viewModel.upgradePrompt = $0 }
69 )) { prompt in
70 Alert(
71 title: Text(prompt.title),
72 message: Text(prompt.message),
73 primaryButton: .default(Text("Open Paywall")) {
74 viewModel.upgradePrompt = nil
75 viewModel.isPaywallPresented = true
76 },
77 secondaryButton: .cancel(Text("Continue")) {
78 viewModel.upgradePrompt = nil
79 }
80 )
81 }
82 .onChange(of: purchaseService.currentTier) { _, newValue in
83 if newValue != .free, selectedTab == .inspect, viewModel.trackedDomains.isEmpty == false {
84 selectedTab = .dashboard
85 }
86 }
87 .sheet(item: $detailDomain) { trackedDomain in
88 NavigationStack {
89 TrackedDomainDetailView(viewModel: viewModel, trackedDomain: trackedDomain)
90 }
91 }
92 .onOpenURL { url in
93 guard let action = DomainDigDeepLink.action(from: url) else { return }
94 perform(action)
95 }
96 .onChange(of: intentRouter.pendingAction) { _, action in
97 consume(action)
98 }
99 .task {
100 consume(intentRouter.pendingAction)
101 }
102 }
103
104 private func consume(_ action: DomainDigDeepLink.Action?) {
105 guard let action else { return }
106 intentRouter.pendingAction = nil
107 perform(action)
108 }
109
110 private func perform(_ action: DomainDigDeepLink.Action) {
111 switch action {
112 case let .inspect(domain):
113 viewModel.domain = domain
114 selectedTab = .inspect
115 viewModel.run()
116 case let .watch(domain):
117 // On success show the dashboard (where tracked domains live); on
118 // failure stay put so the upgrade/paywall alert surfaces in place.
119 if viewModel.trackDomain(domain: domain, availabilityStatus: nil) {
120 selectedTab = .dashboard
121 }
122 case let .detail(domain):
123 // Open the tracked domain's detail (e.g. from a widget tap). If it is
124 // no longer tracked, fall back to inspecting it.
125 if let tracked = viewModel.trackedDomains.first(
126 where: { $0.domain.caseInsensitiveCompare(domain) == .orderedSame }
127 ) {
128 detailDomain = tracked
129 } else {
130 perform(.inspect(domain))
131 }
132 case .sweep:
133 selectedTab = .dashboard
134 viewModel.refreshAllTrackedDomains()
135 }
136 }
137}