Преглед изворни кода

feat: create non-persisting support for URL Hints propogated via an @EnvironmentObject
refactor: move $0.id.caseInsensitiveCompare(active) bit into a helper method "like(String)"

Sam Jaffe пре 3 недеља
родитељ
комит
4d1fb8a62e

+ 4 - 0
Todos/Model/Task.swift

@@ -54,6 +54,10 @@ final class SubTask {
 
 struct Tag : Codable, Identifiable {
   var id: String
+  
+  func like(_ str: String) -> Bool {
+    return id.caseInsensitiveCompare(str) == .orderedSame
+  }
 }
 
 @Model

+ 30 - 0
Todos/Model/URLHint.swift

@@ -0,0 +1,30 @@
+//
+//  URLHint.swift
+//  Todos
+//
+//  Created by Sam Jaffe on 3/1/26.
+//
+
+import Foundation
+import SwiftData
+
+final class URLHint {
+  var prefix: String = ""
+  var replacement: String = ""
+  
+  init() {}
+  
+  init(prefix: String, replacement: String) {
+    self.prefix = prefix
+    self.replacement = replacement
+  }
+  
+  func matches(_ tag: Tag) -> Bool {
+    return tag.id.hasPrefix(prefix)
+  }
+  
+  func url(_ tag: Tag) -> URL {
+    let url = tag.id.replacingOccurrences(of: prefix, with: replacement)
+    return URL(string: url)!
+  }
+}

+ 1 - 0
Todos/TodosApp.swift

@@ -28,5 +28,6 @@ struct TodosApp: App {
         ContentView()
     }
     .modelContainer(sharedModelContainer)
+    .environmentObject(URLHintArray())
   }
 }

+ 1 - 0
Todos/View/CategoryPanelView.swift

@@ -43,4 +43,5 @@ struct CategoryPanelView: View {
 #Preview {
   @Previewable @State var item = Category(timestamp: Date())
   CategoryPanelView(item: item)
+    .environmentObject(URLHintArray())
 }

+ 14 - 2
Todos/View/ContentView.swift

@@ -10,8 +10,12 @@ import SwiftData
 
 struct ContentView: View {
   @Environment(\.modelContext) private var modelContext
+  
   @Query private var items: [Category]
-
+  
+  @State private var showingPopup = false
+  @State private var currentHint: URLHint = URLHint()
+  
   var body: some View {
     NavigationSplitView {
       List {
@@ -35,9 +39,16 @@ struct ContentView: View {
             Label("New Category", systemImage: "plus")
           }
         }
+        ToolbarItem {
+          Button(action: { showingPopup = true }) {
+            Label("New URL Hint", systemImage: "link")
+          }
+        }
       }
     } detail: {
-        Text("Select an item")
+      Text("Select an item")
+    } .popover(isPresented: $showingPopup) {
+      CreateHintPopover(showingPopup: $showingPopup)
     }
   }
 
@@ -70,4 +81,5 @@ struct ContentView: View {
 #Preview {
   ContentView()
       .modelContainer(for: Category.self, inMemory: true)
+      .environmentObject(URLHintArray())
 }

+ 34 - 0
Todos/View/CreateHintPopover.swift

@@ -0,0 +1,34 @@
+//
+//  CreateHintPopover.swift
+//  Todos
+//
+//  Created by Sam Jaffe on 3/1/26.
+//
+
+import SwiftUI
+
+struct CreateHintPopover: View {
+  @Binding var showingPopup: Bool
+  @EnvironmentObject var allHints: URLHintArray
+  @State var currentHint: URLHint = URLHint()
+  
+  var body: some View {
+    TextField("", text: $currentHint.prefix, prompt: Text("Tag Prefix"))
+    TextField("", text: $currentHint.replacement, prompt: Text("URL Form"))
+    HStack {
+      Button(action: {
+        currentHint = URLHint()
+        showingPopup = false
+      }) {
+        Label("Cancel", systemImage: "x.circle")
+      }
+      Button(action: {
+        allHints.array.append(currentHint)
+        currentHint = URLHint()
+        showingPopup = false
+      }) {
+        Label("Submit", systemImage: "checkmark.circle")
+      }
+    }
+  }
+}

+ 19 - 2
Todos/View/TagBarView.swift

@@ -6,15 +6,28 @@
 //
 
 import SwiftUI
+import SwiftData
 
 struct TagBarView: View {
   @Binding var tags: [Tag]
+  @EnvironmentObject var allHints: URLHintArray
+
   @State private var active: String = ""
   @FocusState private var isFocused: Bool
-
+  
   var body: some View {
     HStack {
       ForEach($tags) { tag in
+        let url = allHints.array.filter({ $0.matches(tag.wrappedValue) })
+          .first?.url(tag.wrappedValue)
+        if url != nil {
+          Link(destination: url!) {
+            Label("", systemImage: "link")
+          }
+          .padding(.leading, -8)
+          .padding(.trailing, -10)
+        }
+
         TextField("", text: tag.id)
           .focused($isFocused)
           .onChange(of: isFocused) {
@@ -24,7 +37,7 @@ struct TagBarView: View {
       .scaledToFit()
       TextField("Tag", text: $active)
         .onSubmit {
-          if !tags.contains(where: { $0.id.caseInsensitiveCompare(active) == .orderedSame }) {
+          if !tags.contains(where: { $0.like(active) }) {
             tags.append(Tag(id: active))
           }
           active = ""
@@ -35,5 +48,9 @@ struct TagBarView: View {
 
 #Preview {
   @Previewable @State var tags = Array<Tag>()
+  @Previewable @State var allHints = URLHintArray([
+    URLHint(prefix: "RPD:", replacement: "http://localhost/")
+  ])
   TagBarView(tags: $tags)
+    .environmentObject(allHints)
 }

+ 2 - 1
Todos/View/TaskView.swift

@@ -9,7 +9,7 @@ import SwiftUI
 
 struct TaskView: View {
   @Binding var task: Task
-  
+
   @State private var hideTags: Bool = false
   @State private var hideNotes: Bool = false
   
@@ -82,5 +82,6 @@ struct TaskView: View {
 #Preview {
   @Previewable @State var task = Task(name: "New Task")
   TaskView(task: $task)
+      .environmentObject(URLHintArray())
       .frame(minHeight: 100) // Preview does not resize window properly
 }

+ 20 - 0
Todos/ViewModel/URLHintArray.swift

@@ -0,0 +1,20 @@
+//
+//  URLHintArray.swift
+//  Todos
+//
+//  Created by Sam Jaffe on 3/1/26.
+//
+
+import Foundation
+import SwiftData
+internal import Combine
+
+class URLHintArray : ObservableObject {
+  @Published var array: [URLHint] = [URLHint]()
+  
+  init() {}
+  
+  init(_ array: [URLHint]) {
+    self.array = array
+  }
+}