Add Yahoo Finance stock data integration
- Add StockDataSource to fetch quotes from Yahoo Finance API - Add StockSettingsStore for persisting user's stock symbols - Add StockSettingsView with UI to manage symbols (max 5) - Add STOCK feed item type and ranker weight (0.3) - Integrate stock fetch into ContextOrchestrator pipeline - Stock cards appear in FYI bucket and sync to Glass via BLE 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -90,6 +90,7 @@ final class HeuristicRanker {
|
||||
case .transit: return 0.75
|
||||
case .poiNearby: return 0.6
|
||||
case .info: return 0.4
|
||||
case .stock: return 0.3
|
||||
case .nowPlaying: return 0.25
|
||||
case .currentWeather: return 0.0
|
||||
case .allQuiet: return 0.0
|
||||
|
||||
48
IrisCompanion/iris/Models/StockSettingsStore.swift
Normal file
48
IrisCompanion/iris/Models/StockSettingsStore.swift
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// StockSettingsStore.swift
|
||||
// iris
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
final class StockSettingsStore: ObservableObject {
|
||||
nonisolated static let userDefaultsKey = "iris.stock.symbols"
|
||||
|
||||
@Published private(set) var symbols: [String] = []
|
||||
|
||||
private let maxSymbols = 5
|
||||
|
||||
init() {
|
||||
loadSymbols()
|
||||
}
|
||||
|
||||
private func loadSymbols() {
|
||||
symbols = UserDefaults.standard.stringArray(forKey: Self.userDefaultsKey) ?? []
|
||||
}
|
||||
|
||||
func saveSymbols(_ newSymbols: [String]) {
|
||||
let cleaned = newSymbols
|
||||
.map { $0.uppercased().trimmingCharacters(in: .whitespacesAndNewlines) }
|
||||
.filter { !$0.isEmpty }
|
||||
.prefix(maxSymbols)
|
||||
symbols = Array(cleaned)
|
||||
UserDefaults.standard.set(symbols, forKey: Self.userDefaultsKey)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func addSymbol(_ symbol: String) -> Bool {
|
||||
guard symbols.count < maxSymbols else { return false }
|
||||
let cleaned = symbol.uppercased().trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !cleaned.isEmpty, !symbols.contains(cleaned) else { return false }
|
||||
symbols.append(cleaned)
|
||||
UserDefaults.standard.set(symbols, forKey: Self.userDefaultsKey)
|
||||
return true
|
||||
}
|
||||
|
||||
func removeSymbol(_ symbol: String) {
|
||||
symbols.removeAll { $0 == symbol.uppercased() }
|
||||
UserDefaults.standard.set(symbols, forKey: Self.userDefaultsKey)
|
||||
}
|
||||
}
|
||||
@@ -16,5 +16,6 @@ enum FeedItemType: String, Codable, CaseIterable {
|
||||
case nowPlaying = "NOW_PLAYING"
|
||||
case currentWeather = "CURRENT_WEATHER"
|
||||
case calendarEvent = "CALENDAR_EVENT"
|
||||
case stock = "STOCK"
|
||||
case allQuiet = "ALL_QUIET"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user