Initial project setup - Phases 1-3 complete
This commit is contained in:
144
UI/More/BlockListView.swift
Normal file
144
UI/More/BlockListView.swift
Normal file
@@ -0,0 +1,144 @@
|
||||
import SwiftUI
|
||||
import ProxyCore
|
||||
import GRDB
|
||||
|
||||
struct BlockListView: View {
|
||||
@State private var isEnabled = IPCManager.shared.isBlockListEnabled
|
||||
@State private var entries: [BlockListEntry] = []
|
||||
@State private var showAddRule = false
|
||||
@State private var observation: AnyDatabaseCancellable?
|
||||
|
||||
private let rulesRepo = RulesRepository()
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section {
|
||||
ToggleHeaderView(
|
||||
title: "Block List",
|
||||
description: "Block requests matching these rules. Blocked requests will be dropped or hidden based on the action.",
|
||||
isEnabled: $isEnabled
|
||||
)
|
||||
.onChange(of: isEnabled) { _, newValue in
|
||||
IPCManager.shared.isBlockListEnabled = newValue
|
||||
IPCManager.shared.post(.configurationChanged)
|
||||
}
|
||||
}
|
||||
.listRowInsets(EdgeInsets())
|
||||
|
||||
Section("Rules") {
|
||||
if entries.isEmpty {
|
||||
Text("No block rules")
|
||||
.foregroundStyle(.secondary)
|
||||
.font(.subheadline)
|
||||
} else {
|
||||
ForEach(entries) { entry in
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(entry.name ?? entry.urlPattern)
|
||||
.font(.subheadline.weight(.medium))
|
||||
Text(entry.urlPattern)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
Text(entry.action.displayName)
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.tertiary)
|
||||
}
|
||||
}
|
||||
.onDelete { indexSet in
|
||||
for index in indexSet {
|
||||
if let id = entries[index].id {
|
||||
try? rulesRepo.deleteBlockEntry(id: id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Block List")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button { showAddRule = true } label: {
|
||||
Image(systemName: "plus")
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showAddRule) {
|
||||
NewBlockRuleView { entry in
|
||||
var entry = entry
|
||||
try? rulesRepo.insertBlockEntry(&entry)
|
||||
}
|
||||
}
|
||||
.task {
|
||||
observation = rulesRepo.observeBlockListEntries()
|
||||
.start(in: DatabaseManager.shared.dbPool) { error in
|
||||
print("Block list observation error: \(error)")
|
||||
} onChange: { newEntries in
|
||||
entries = newEntries
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - New Block Rule
|
||||
|
||||
struct NewBlockRuleView: View {
|
||||
let onSave: (BlockListEntry) -> Void
|
||||
|
||||
@State private var name = ""
|
||||
@State private var urlPattern = ""
|
||||
@State private var method = "ANY"
|
||||
@State private var includeSubpaths = true
|
||||
@State private var blockAction: BlockAction = .blockAndHide
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
Section {
|
||||
TextField("Name (optional)", text: $name)
|
||||
TextField("URL Pattern", text: $urlPattern)
|
||||
.textInputAutocapitalization(.never)
|
||||
.autocorrectionDisabled()
|
||||
}
|
||||
|
||||
Section {
|
||||
Picker("Method", selection: $method) {
|
||||
Text("ANY").tag("ANY")
|
||||
ForEach(ProxyConstants.httpMethods, id: \.self) { m in
|
||||
Text(m).tag(m)
|
||||
}
|
||||
}
|
||||
Toggle("Include Subpaths", isOn: $includeSubpaths)
|
||||
}
|
||||
|
||||
Section {
|
||||
Picker("Block Action", selection: $blockAction) {
|
||||
ForEach(BlockAction.allCases, id: \.self) { action in
|
||||
Text(action.displayName).tag(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("New Block Rule")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Cancel") { dismiss() }
|
||||
}
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button("Save") {
|
||||
let entry = BlockListEntry(
|
||||
name: name.isEmpty ? nil : name,
|
||||
urlPattern: urlPattern,
|
||||
method: method,
|
||||
includeSubpaths: includeSubpaths,
|
||||
blockAction: blockAction
|
||||
)
|
||||
onSave(entry)
|
||||
dismiss()
|
||||
}
|
||||
.disabled(urlPattern.isEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user