diff options
| author | Fuwn <[email protected]> | 2025-04-23 02:27:13 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2025-04-23 02:27:13 -0700 |
| commit | a01103249958dacbd40391b99c747aa53200b127 (patch) | |
| tree | 8a5760812e85af326e7ffeef7341e26f91e4261d | |
| parent | chore: Add SwiftLint rules (diff) | |
| download | holobar-a01103249958dacbd40391b99c747aa53200b127.tar.xz holobar-a01103249958dacbd40391b99c747aa53200b127.zip | |
style: Format all source files
| -rw-r--r-- | HoloBar/Character.swift | 12 | ||||
| -rw-r--r-- | HoloBar/CharacterFetcher.swift | 209 | ||||
| -rw-r--r-- | HoloBar/HoloBarApp.swift | 118 |
3 files changed, 171 insertions, 168 deletions
diff --git a/HoloBar/Character.swift b/HoloBar/Character.swift index d16b133..0bad13e 100644 --- a/HoloBar/Character.swift +++ b/HoloBar/Character.swift @@ -1,10 +1,10 @@ import Foundation struct Character: Identifiable { - let id = UUID() - let name: String - let profileURL: URL - let avatarURL: URL - let rawName: String - let affiliation: String + let id = UUID() + let name: String + let profileURL: URL + let avatarURL: URL + let rawName: String + let affiliation: String } diff --git a/HoloBar/CharacterFetcher.swift b/HoloBar/CharacterFetcher.swift index 7ba6854..ff5a059 100644 --- a/HoloBar/CharacterFetcher.swift +++ b/HoloBar/CharacterFetcher.swift @@ -2,124 +2,127 @@ import Foundation import SwiftSoup class CharacterFetcher: ObservableObject { - @Published var characters: [Character] = [] + @Published var characters: [Character] = [] - init() { - let today = Calendar.current.dateComponents([.month, .day], from: Date()) + init() { + let today = Calendar.current.dateComponents([.month, .day], from: Date()) - if let month = today.month, let day = today.day { - fetchCharacters(for: month, day: day) - } + if let month = today.month, let day = today.day { + fetchCharacters(for: month, day: day) } + } - func fetchCharacters(for month: Int, day: Int) { - let urlString = "https://hololist.net/birthday/?birthday_month=\(String(format: "%02d", month))&birthday_day=\(String(format: "%02d", day))" - guard let url = URL(string: urlString) else { return } + func fetchCharacters(for month: Int, day: Int) { + let urlString = + "https://hololist.net/birthday/?birthday_month=\(String(format: "%02d", month))&birthday_day=\(String(format: "%02d", day))" + guard let url = URL(string: urlString) else { return } - let task = URLSession.shared.dataTask(with: url) { data, _, error in - guard let data = data, error == nil, - let html = String(data: data, encoding: .utf8) else { return } + let task = URLSession.shared.dataTask(with: url) { data, _, error in + guard let data = data, error == nil, + let html = String(data: data, encoding: .utf8) + else { return } - DispatchQueue.main.async { - self.fetchAffiliations(for: self.parseHTML(html: html)) - } - } + DispatchQueue.main.async { + self.fetchAffiliations(for: self.parseHTML(html: html)) + } + } - task.resume() + task.resume() + } + + private func parseHTML(html: String) -> [Character] { + var fetchedCharacters: [Character] = [] + + do { + let document = try SwiftSoup.parse(html) + let characterElements = try document.select("div.d-flex.mb-4.rounded") + + for element in characterElements { + if let nameElement = try? element.select("a.line-truncate span").first(), + let linkElement = try? element.select("a.line-truncate").first(), + let profileHref = try? linkElement.attr("href"), + let profileURL = URL(string: profileHref) + { + let name = try nameElement.text() + + fetchedCharacters + .append( + Character( + name: name, + profileURL: profileURL, + avatarURL: profileURL, // Avatar URLs are fetched in ``fetchAffiliations`` + rawName: name, + affiliation: "" + ) + ) + } + } + } catch { + let blankURL = URL(string: "#")! + + fetchedCharacters + .append( + Character( + name: "Error parsing HTML", + profileURL: blankURL, + avatarURL: blankURL, + rawName: "", + affiliation: "" + ) + ) } - private func parseHTML(html: String) -> [Character] { - var fetchedCharacters: [Character] = [] + return fetchedCharacters + } + + private func fetchAffiliations(for characters: [Character]) { + let group = DispatchGroup() + var updatedCharacters: [Character] = [] + + for character in characters { + group.enter() + + let task = URLSession.shared.dataTask(with: character.profileURL) { + data, + _, + error in + defer { group.leave() } + + guard let data = data, + error == nil, + let html = String(data: data, encoding: .utf8) + else { return } do { - let document = try SwiftSoup.parse(html) - let characterElements = try document.select("div.d-flex.mb-4.rounded") - - for element in characterElements { - if let nameElement = try? element.select("a.line-truncate span").first(), - let linkElement = try? element.select("a.line-truncate").first(), - let profileHref = try? linkElement.attr("href"), - let profileURL = URL(string: profileHref) - { - let name = try nameElement.text() - - fetchedCharacters - .append( - Character( - name: name, - profileURL: profileURL, - avatarURL: profileURL, // Avatar URLs are fetched in ``fetchAffiliations`` - rawName: name, - affiliation: "" - ) - ) - } - } - } catch { - let blankURL = URL(string: "#")! - - fetchedCharacters - .append( - Character( - name: "Error parsing HTML", - profileURL: blankURL, - avatarURL: blankURL, - rawName: "", - affiliation: "" - ) + let document = try SwiftSoup.parse(html) + + if let affiliationElement = try? document.select("#affiliation a").first(), + let affiliation = try? affiliationElement.text() + { + updatedCharacters + .append( + Character( + name: "\(character.name) (\(affiliation))", + profileURL: character.profileURL, + avatarURL: (try? document.select("#left img").first()?.attr("data-src")) + .flatMap { URL(string: $0) } ?? character.avatarURL, + rawName: character.name, + affiliation: affiliation ) + ) + } else { + updatedCharacters.append(character) + } + } catch { + updatedCharacters.append(character) } + } - return fetchedCharacters + task.resume() } - private func fetchAffiliations(for characters: [Character]) { - let group = DispatchGroup() - var updatedCharacters: [Character] = [] - - for character in characters { - group.enter() - - let task = URLSession.shared.dataTask(with: character.profileURL) { - data, - _, - error in - defer { group.leave() } - - guard let data = data, - error == nil, - let html = String(data: data, encoding: .utf8) else { return } - - do { - let document = try SwiftSoup.parse(html) - - if let affiliationElement = try? document.select("#affiliation a").first(), - let affiliation = try? affiliationElement.text() - { - updatedCharacters - .append( - Character( - name: "\(character.name) (\(affiliation))", - profileURL: character.profileURL, - avatarURL: (try? document.select("#left img").first()?.attr("data-src")) - .flatMap { URL(string: $0) } ?? character.avatarURL, - rawName: character.name, - affiliation: affiliation - ) - ) - } else { - updatedCharacters.append(character) - } - } catch { - updatedCharacters.append(character) - } - } - - task.resume() - } - - group.notify(queue: .main) { - self.characters = updatedCharacters - } + group.notify(queue: .main) { + self.characters = updatedCharacters } + } } diff --git a/HoloBar/HoloBarApp.swift b/HoloBar/HoloBarApp.swift index eac0acd..650495b 100644 --- a/HoloBar/HoloBarApp.swift +++ b/HoloBar/HoloBarApp.swift @@ -2,75 +2,75 @@ import SwiftUI @main struct HoloBarApp: App { - @StateObject private var fetcher = CharacterFetcher() - @State private var showAvatars = false + @StateObject private var fetcher = CharacterFetcher() + @State private var showAvatars = false - var body: some Scene { - MenuBarExtra { - VStack { - if fetcher.characters.isEmpty { - Text("Loading …") - } else { - ForEach(fetcher.characters) { character in - Button(action: { - NSWorkspace.shared.open(character.profileURL) - }) { - HStack { - if showAvatars { - AsyncImage(url: character.avatarURL) { phase in - if let image = phase.image { - image - .resizable() - .scaledToFill() - } else if phase.error != nil { - Image(systemName: "person.crop.circle.badge.exclamationmark") - .resizable() - .scaledToFit() - .foregroundStyle(.gray) - } else { - Image(systemName: "person.crop.circle") - .resizable() - .scaledToFit() - .foregroundStyle(.gray) - } - } - } - - Text(character.name) - } - } + var body: some Scene { + MenuBarExtra { + VStack { + if fetcher.characters.isEmpty { + Text("Loading …") + } else { + ForEach(fetcher.characters) { character in + Button(action: { + NSWorkspace.shared.open(character.profileURL) + }) { + HStack { + if showAvatars { + AsyncImage(url: character.avatarURL) { phase in + if let image = phase.image { + image + .resizable() + .scaledToFill() + } else if phase.error != nil { + Image(systemName: "person.crop.circle.badge.exclamationmark") + .resizable() + .scaledToFit() + .foregroundStyle(.gray) + } else { + Image(systemName: "person.crop.circle") + .resizable() + .scaledToFit() + .foregroundStyle(.gray) } + } } - Divider() + Text(character.name) + } + } + } + } - #if DEBUG - Button("Simulate Day Change") { - NotificationCenter.default.post(name: .NSCalendarDayChanged, object: nil) - } - #endif + Divider() - Button("\(showAvatars ? "Hide" : "Show") Avatars") { - showAvatars.toggle() - } + #if DEBUG + Button("Simulate Day Change") { + NotificationCenter.default.post(name: .NSCalendarDayChanged, object: nil) + } + #endif - Button("Refresh", action: refreshCharacters) - Button("Quit", action: { NSApplication.shared.terminate(nil) }) - } - .onReceive(NotificationCenter.default.publisher(for: .NSCalendarDayChanged)) { _ in - refreshCharacters() - } - } label: { - Text("HL") + Button("\(showAvatars ? "Hide" : "Show") Avatars") { + showAvatars.toggle() } + + Button("Refresh", action: refreshCharacters) + Button("Quit", action: { NSApplication.shared.terminate(nil) }) + } + .onReceive(NotificationCenter.default.publisher(for: .NSCalendarDayChanged)) { _ in + refreshCharacters() + } + } label: { + Text("HL") } + } - private func refreshCharacters() { - let today = Calendar.current.dateComponents([.month, .day], from: Date()) + private func refreshCharacters() { + let today = Calendar.current.dateComponents([.month, .day], from: Date()) - if let month = today.month, let day = today.day { - fetcher.characters.removeAll() - fetcher.fetchCharacters(for: month, day: day) - } + if let month = today.month, let day = today.day { + fetcher.characters.removeAll() + fetcher.fetchCharacters(for: month, day: day) } + } } |