import Foundation import Observation import OSLog private let feedLogger = Logger(subsystem: "com.treyt.mlbTVOS", category: "Feed") private func logFeed(_ message: String) { feedLogger.debug("\(message, privacy: .public)") print("[Feed] \(message)") } enum FeedItemType: Sendable { case news case transaction case scoring } struct FeedItem: Identifiable, Sendable { let id: String let type: FeedItemType let title: String let subtitle: String let teamCode: String? let timestamp: Date } @Observable @MainActor final class FeedViewModel { var items: [FeedItem] = [] var isLoading = false @ObservationIgnored private var refreshTask: Task? private let webService = MLBWebDataService() func loadFeed() async { isLoading = true logFeed("loadFeed start") async let newsTask = webService.fetchNewsHeadlines() async let transactionsTask = webService.fetchTransactions() let news = await newsTask let transactions = await transactionsTask var allItems: [FeedItem] = [] // News for headline in news { allItems.append(FeedItem( id: "news-\(headline.id)", type: .news, title: headline.title, subtitle: headline.summary, teamCode: nil, timestamp: headline.timestamp )) } // Transactions for tx in transactions { allItems.append(FeedItem( id: "tx-\(tx.id)", type: .transaction, title: tx.description, subtitle: tx.type, teamCode: tx.teamCode.isEmpty ? nil : tx.teamCode, timestamp: tx.date )) } // Sort reverse chronological items = allItems.sorted { $0.timestamp > $1.timestamp } isLoading = false logFeed("loadFeed complete items=\(items.count)") } func startAutoRefresh() { stopAutoRefresh() refreshTask = Task { [weak self] in while !Task.isCancelled { try? await Task.sleep(for: .seconds(300)) guard !Task.isCancelled, let self else { break } await self.loadFeed() } } } func stopAutoRefresh() { refreshTask?.cancel() refreshTask = nil } }