summaryrefslogtreecommitdiff
path: root/DomainDig/WatchlistView.swift
diff options
context:
space:
mode:
Diffstat (limited to 'DomainDig/WatchlistView.swift')
-rw-r--r--DomainDig/WatchlistView.swift96
1 files changed, 93 insertions, 3 deletions
diff --git a/DomainDig/WatchlistView.swift b/DomainDig/WatchlistView.swift
index 31c6202..d092f65 100644
--- a/DomainDig/WatchlistView.swift
+++ b/DomainDig/WatchlistView.swift
@@ -6,6 +6,10 @@ struct WatchlistView: View {
@Environment(\.dismiss) private var dismiss
@State private var purchaseService = PurchaseService.shared
@State private var showWorkflowAddSheet = false
+ @State private var showAddDomainSheet = false
+ @State private var newTrackedDomain = ""
+ @State private var addDomainError: String?
+ @FocusState private var isAddDomainFieldFocused: Bool
private var pinnedDomains: [TrackedDomain] {
viewModel.filteredTrackedDomains.filter(\.isPinned)
@@ -53,7 +57,8 @@ struct WatchlistView: View {
title: "No Tracked Domains",
message: "Track important domains locally so you can refresh them quickly and see status changes at a glance.",
suggestion: "Run an inspection and use the Track action on a domain you care about.",
- systemImage: "eye"
+ systemImage: "eye",
+ showsCardBackground: false
)
}
.listRowBackground(Color(.systemGray6).opacity(0.5))
@@ -82,8 +87,16 @@ struct WatchlistView: View {
.navigationTitle("Watchlist")
.searchable(text: $viewModel.watchlistSearchText, prompt: "Search tracked domains")
.toolbar {
- if !viewModel.filteredTrackedDomains.isEmpty {
- ToolbarItemGroup(placement: .topBarTrailing) {
+ ToolbarItemGroup(placement: .topBarTrailing) {
+ Button {
+ addDomainError = nil
+ newTrackedDomain = ""
+ showAddDomainSheet = true
+ } label: {
+ Image(systemName: "plus")
+ }
+
+ if !viewModel.filteredTrackedDomains.isEmpty {
Menu {
Picker("Filter", selection: $viewModel.watchlistFilter) {
ForEach(WatchlistFilterOption.allCases) { option in
@@ -139,6 +152,52 @@ struct WatchlistView: View {
.sheet(item: batchSummaryBinding) { summary in
BatchSweepSummaryView(viewModel: viewModel, summary: summary)
}
+ .sheet(isPresented: $showAddDomainSheet) {
+ NavigationStack {
+ Form {
+ Section("Domain") {
+ TextField("example.com", text: $newTrackedDomain)
+ .textInputAutocapitalization(.never)
+ .autocorrectionDisabled()
+ .keyboardType(.URL)
+ .textContentType(.URL)
+ .focused($isAddDomainFieldFocused)
+ .onSubmit(addTrackedDomain)
+ }
+
+ Section {
+ Text("Adds the domain directly to your watchlist so monitoring can run without a prior inspection.")
+ .font(appDensity.font(.caption))
+ .foregroundStyle(.secondary)
+ }
+
+ if let addDomainError {
+ Section {
+ Text(addDomainError)
+ .font(appDensity.font(.caption))
+ .foregroundStyle(.red)
+ }
+ }
+ }
+ .navigationTitle("Add Domain")
+ .toolbar {
+ ToolbarItem(placement: .cancellationAction) {
+ Button("Cancel") {
+ showAddDomainSheet = false
+ }
+ }
+
+ ToolbarItem(placement: .confirmationAction) {
+ Button("Add", action: addTrackedDomain)
+ }
+ }
+ .onAppear {
+ DispatchQueue.main.async {
+ isAddDomainFieldFocused = true
+ }
+ }
+ }
+ }
.sheet(isPresented: $showWorkflowAddSheet) {
WorkflowBulkAddSheet(
viewModel: viewModel,
@@ -271,6 +330,24 @@ struct WatchlistView: View {
ExportPresenter.share(filename: filename, data: data)
}
+
+ private func addTrackedDomain() {
+ let draft = newTrackedDomain.trimmingCharacters(in: .whitespacesAndNewlines)
+ guard !draft.isEmpty else {
+ addDomainError = "Enter a domain to add."
+ return
+ }
+
+ if viewModel.trackDomain(domain: draft, availabilityStatus: nil) {
+ AppHaptics.track()
+ isAddDomainFieldFocused = false
+ addDomainError = nil
+ newTrackedDomain = ""
+ showAddDomainSheet = false
+ } else if viewModel.upgradePrompt == nil {
+ addDomainError = "Enter a valid domain like example.com."
+ }
+ }
}
struct WatchlistRowView: View {
@@ -468,6 +545,19 @@ struct TrackedDomainDetailView: View {
}
.listRowBackground(Color(.systemGray6).opacity(0.5))
+ Section("Monitoring Status") {
+ LabeledContent("State", value: viewModel.monitoringStatusLabel(for: liveTrackedDomain))
+ LabeledContent("Current Interval", value: viewModel.monitoringIntervalLabel(for: liveTrackedDomain))
+ LabeledContent(
+ "Last Change",
+ value: liveTrackedDomain.monitoringState.lastChangeDate?.formatted(date: .abbreviated, time: .shortened) ?? "None"
+ )
+ if !liveTrackedDomain.pendingMonitoringAlerts.isEmpty {
+ LabeledContent("Queued Alerts", value: "\(liveTrackedDomain.pendingMonitoringAlerts.count)")
+ }
+ }
+ .listRowBackground(Color(.systemGray6).opacity(0.5))
+
if let summary = viewModel.latestChangeSummary(for: liveTrackedDomain) {
Section("Latest Change Summary") {
DomainChangeSummaryView(summary: summary)