Initial project setup - Phases 1-3 complete

This commit is contained in:
Trey t
2026-04-06 11:28:40 -05:00
commit c77e506db5
293 changed files with 14233 additions and 0 deletions

144
UI/More/BlockListView.swift Normal file
View 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)
}
}
}
}
}