aboutsummaryrefslogtreecommitdiff
path: root/HoloBar/ContentView.swift
diff options
context:
space:
mode:
authorFuwn <[email protected]>2025-02-12 23:25:10 -0800
committerFuwn <[email protected]>2025-02-12 23:25:10 -0800
commitee6cc230c73a4bad1df711a0cd0352af7e4baced (patch)
treeceb5f14e3d30292af7cc501039738874c5374b39 /HoloBar/ContentView.swift
parentfeat(HoloBarApp): Add more avatar fallback levels (diff)
downloadholobar-ui.tar.xz
holobar-ui.zip
feat: Add a UI windowui
Diffstat (limited to 'HoloBar/ContentView.swift')
-rw-r--r--HoloBar/ContentView.swift70
1 files changed, 70 insertions, 0 deletions
diff --git a/HoloBar/ContentView.swift b/HoloBar/ContentView.swift
new file mode 100644
index 0000000..784f45e
--- /dev/null
+++ b/HoloBar/ContentView.swift
@@ -0,0 +1,70 @@
+import SwiftUI
+
+struct ContentView: View {
+ @EnvironmentObject var fetcher: CharacterFetcher
+ @State private var searchText: String = ""
+
+ private var filteredCharacters: [Character] {
+ if searchText.isEmpty { return fetcher.characters }
+
+ return fetcher.characters.filter {
+ $0.name.localizedCaseInsensitiveContains(searchText)
+ }
+ }
+
+ var body: some View {
+ NavigationView {
+ List(filteredCharacters) { character in
+ Button {
+ NSWorkspace.shared.open(character.profileURL)
+ } label: {
+ HStack(spacing: 16) {
+ AsyncImage(url: character.avatarURL) { phase in
+ if let image = phase.image {
+ image
+ .resizable()
+ .aspectRatio(contentMode: .fill)
+ } else if phase.error != nil {
+ Image(systemName: "person.crop.circle.badge.exclamationmark")
+ .resizable()
+ .aspectRatio(contentMode: .fill)
+ .foregroundStyle(.gray)
+ } else {
+ Image(systemName: "person.crop.circle")
+ .resizable()
+ .aspectRatio(contentMode: .fill)
+ .foregroundStyle(.gray)
+ .opacity(0.25)
+ }
+ }
+ .frame(width: 40, height: 40)
+ .clipShape(Circle())
+ .overlay(
+ Circle().stroke(Color.secondary.opacity(0.3), lineWidth: 1)
+ )
+
+ VStack(alignment: .leading, spacing: 4) {
+ Text(character.rawName).font(.headline)
+
+ Text(character.affiliation)
+ .font(.subheadline)
+ .foregroundColor(.secondary)
+ }
+ }
+ .padding(.vertical, 8)
+ }
+ .buttonStyle(PlainButtonStyle())
+ }
+ .navigationTitle("Characters")
+ .searchable(text: $searchText, placement: .automatic, prompt: "Search Characters")
+ .toolbar {
+ ToolbarItem {
+ Button(action: fetcher.refreshCharactersForToday) {
+ Image(systemName: "arrow.clockwise")
+ }
+ .help("Refresh Characters")
+ }
+ }
+ }
+ }
+}