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:
104
IrisCompanion/iris/Views/StockSettingsView.swift
Normal file
104
IrisCompanion/iris/Views/StockSettingsView.swift
Normal file
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// StockSettingsView.swift
|
||||
// iris
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct StockSettingsView: View {
|
||||
@ObservedObject var store: StockSettingsStore
|
||||
@State private var newSymbol: String = ""
|
||||
@State private var showError: Bool = false
|
||||
@State private var errorMessage: String = ""
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section {
|
||||
ForEach(store.symbols, id: \.self) { symbol in
|
||||
HStack {
|
||||
Text(symbol)
|
||||
.font(.body.monospaced())
|
||||
Spacer()
|
||||
Button(role: .destructive) {
|
||||
store.removeSymbol(symbol)
|
||||
} label: {
|
||||
Image(systemName: "trash")
|
||||
.foregroundStyle(.red)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
.onDelete { indexSet in
|
||||
for index in indexSet {
|
||||
store.removeSymbol(store.symbols[index])
|
||||
}
|
||||
}
|
||||
|
||||
if store.symbols.count < 5 {
|
||||
HStack {
|
||||
TextField("Symbol (e.g. AAPL)", text: $newSymbol)
|
||||
.textInputAutocapitalization(.characters)
|
||||
.autocorrectionDisabled()
|
||||
.font(.body.monospaced())
|
||||
Button("Add") {
|
||||
addSymbol()
|
||||
}
|
||||
.disabled(newSymbol.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Stock Symbols")
|
||||
} footer: {
|
||||
Text("Enter up to 5 stock symbols. Cards appear in the FYI section.")
|
||||
}
|
||||
|
||||
Section {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("Examples:")
|
||||
.font(.subheadline.bold())
|
||||
Text("AAPL - Apple Inc.")
|
||||
Text("GOOGL - Alphabet Inc.")
|
||||
Text("^GSPC - S&P 500 Index")
|
||||
Text("^DJI - Dow Jones")
|
||||
Text("^IXIC - NASDAQ Composite")
|
||||
}
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Stocks")
|
||||
.alert("Error", isPresented: $showError) {
|
||||
Button("OK") {}
|
||||
} message: {
|
||||
Text(errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
private func addSymbol() {
|
||||
let cleaned = newSymbol.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
|
||||
guard !cleaned.isEmpty else { return }
|
||||
|
||||
if store.symbols.contains(cleaned) {
|
||||
errorMessage = "Symbol '\(cleaned)' already exists."
|
||||
showError = true
|
||||
return
|
||||
}
|
||||
|
||||
if store.symbols.count >= 5 {
|
||||
errorMessage = "Maximum of 5 symbols allowed."
|
||||
showError = true
|
||||
return
|
||||
}
|
||||
|
||||
store.addSymbol(cleaned)
|
||||
newSymbol = ""
|
||||
}
|
||||
}
|
||||
|
||||
struct StockSettingsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
StockSettingsView(store: StockSettingsStore())
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user