- 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>
105 lines
3.3 KiB
Swift
105 lines
3.3 KiB
Swift
//
|
|
// 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())
|
|
}
|
|
}
|
|
}
|