feat: rebuild iOS app from API audit + new podcast/media service
All checks were successful
Security Checks / dependency-audit (push) Successful in 12s
Security Checks / secret-scanning (push) Successful in 3s
Security Checks / dockerfile-lint (push) Successful in 3s

iOS App (complete rebuild):
- Audited all fitness API endpoints against live responses
- Models match exact API field names (snapshot_ prefixes, UUID strings)
- FoodEntry uses computed properties (foodName, calories, etc.) wrapping snapshot fields
- Flexible Int/Double decoding for all numeric fields
- AI assistant with raw JSON state management (JSONSerialization, not Codable)
- Home dashboard with custom background, frosted glass calorie widget
- Fitness: Today/Templates/Goals/Foods tabs
- Food search with recent + all sections
- Meal sections with colored accent bars, swipe to delete
- 120fps ProMotion, iOS 17+ @Observable

Podcast/Media Service:
- FastAPI backend for podcast RSS + local audiobook folders
- Shows, episodes, playback progress, queue management
- RSS feed fetching with feedparser + ETag support
- Local folder scanning with mutagen for audio metadata
- HTTP Range streaming for local audio files
- Playback events logging (play/pause/seek/complete)
- Reuses brain's PostgreSQL + Redis
- media_ prefixed tables

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yusuf Suleman
2026-04-03 02:36:43 -05:00
parent e350a354a3
commit 69af4b84a5
56 changed files with 4256 additions and 4620 deletions

View File

@@ -1,37 +1,90 @@
import SwiftUI
struct ContentView: View {
@Environment(AuthManager.self) private var authManager
@Environment(AuthManager.self) private var auth
var body: some View {
Group {
if authManager.isCheckingAuth {
LoadingView(message: "Checking session...")
} else if authManager.isLoggedIn {
if auth.isCheckingAuth {
ProgressView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.canvas)
} else if auth.isLoggedIn {
MainTabView()
} else {
LoginView()
}
}
.task {
await authManager.checkAuth()
await auth.checkAuth()
}
}
}
struct MainTabView: View {
var body: some View {
TabView {
HomeView()
.tabItem {
Label("Home", systemImage: "house.fill")
}
@State private var selectedTab = 0
@State private var showAssistant = false
FitnessTabView()
.tabItem {
Label("Fitness", systemImage: "flame.fill")
}
var body: some View {
ZStack(alignment: .bottomTrailing) {
TabView(selection: $selectedTab) {
HomeView()
.tabItem {
Label("Home", systemImage: "house.fill")
}
.tag(0)
FitnessTabView()
.tabItem {
Label("Fitness", systemImage: "flame.fill")
}
.tag(1)
}
.tint(Color.accentWarm)
Button {
showAssistant = true
} label: {
Image(systemName: "plus")
.font(.title2.weight(.semibold))
.foregroundStyle(.white)
.frame(width: 56, height: 56)
.background(Color.accentWarm)
.clipShape(Circle())
.shadow(color: .black.opacity(0.2), radius: 8, y: 4)
}
.padding(.trailing, 20)
.padding(.bottom, 70)
}
.sheet(isPresented: $showAssistant) {
AssistantSheetView()
}
.tint(Color.accentWarm)
}
}
struct AssistantSheetView: View {
@State private var selectedMode = 0
var body: some View {
NavigationStack {
VStack(spacing: 0) {
Picker("Mode", selection: $selectedMode) {
Text("AI Chat").tag(0)
Text("Quick Add").tag(1)
}
.pickerStyle(.segmented)
.padding(.horizontal)
.padding(.top, 8)
if selectedMode == 0 {
AssistantChatView()
} else {
FoodSearchView(isSheet: true)
}
}
.navigationTitle("Add Food")
.navigationBarTitleDisplayMode(.inline)
}
.presentationDetents([.large])
}
}