diff --git a/ios/Platform/Platform/ContentView.swift b/ios/Platform/Platform/ContentView.swift index 6129106..5d4d14b 100644 --- a/ios/Platform/Platform/ContentView.swift +++ b/ios/Platform/Platform/ContentView.swift @@ -32,46 +32,53 @@ struct MainTabView: View { @State private var scrollSpeed: Double = 1.0 private var showReader: Bool { - auth.currentUser?.id != 4 // Madiha doesn't see Reader + auth.currentUser?.id != 4 } var body: some View { ZStack { TabView(selection: $selectedTab) { - HomeView(selectedTab: $selectedTab) - .tabItem { Label("Home", systemImage: "house.fill") } - .tag(0) + Tab("Home", systemImage: "house.fill", value: 0) { + HomeView(selectedTab: $selectedTab) + } - FitnessTabView() - .tabItem { Label("Fitness", systemImage: "flame.fill") } - .tag(1) + Tab("Fitness", systemImage: "flame.fill", value: 1) { + FitnessTabView() + } if showReader { - ReaderTabView(vm: readerVM, isAutoScrolling: $isAutoScrolling, scrollSpeed: $scrollSpeed) - .tabItem { Label("Reader", systemImage: "newspaper.fill") } - .tag(2) + Tab("Reader", systemImage: "newspaper.fill", value: 2) { + ReaderTabView(vm: readerVM, isAutoScrolling: $isAutoScrolling, scrollSpeed: $scrollSpeed) + } + + // Auto-scroll button — uses .search role for the separated + // circular placement on the trailing side of the tab bar + Tab("Auto", systemImage: "play.fill", value: 3, role: .search) { + // When tapped, toggle auto-scroll instead of showing search + Color.clear + .onAppear { + // Switch to Reader tab and start auto-scroll + selectedTab = 2 + withAnimation(.spring(duration: 0.3)) { + isAutoScrolling = true + } + } + } } } .tint(Color.accentWarm) .modifier(TabBarMinimizeModifier()) - // Floating buttons - VStack { - Spacer() - HStack(alignment: .bottom) { - if selectedTab != 2 { + // Floating buttons (hidden on Reader tab) + if selectedTab != 2 { + VStack { + Spacer() + HStack(alignment: .bottom) { FeedbackButton() .padding(.leading, 20) - } - Spacer() + Spacer() - if selectedTab == 2 { - // Reader: auto-scroll control at tab bar level - readerAutoScrollPill - .padding(.trailing, 8) - .padding(.bottom, 0) - } else { Button { showAssistant = true } label: { Image(systemName: "plus") .font(.title2.weight(.semibold)) @@ -83,8 +90,61 @@ struct MainTabView: View { } .padding(.trailing, 20) } + .padding(.bottom, 70) } - .padding(.bottom, 70) + } + + // Auto-scroll speed controls (overlay when playing) + if isAutoScrolling && selectedTab == 2 { + VStack { + Spacer() + HStack(spacing: 12) { + Button { + scrollSpeed = max(0.25, scrollSpeed - 0.25) + } label: { + Image(systemName: "minus") + .font(.caption.weight(.bold)) + .foregroundStyle(Color.accentWarm) + .frame(width: 32, height: 32) + } + + Text(String(format: "%.2fx", scrollSpeed)) + .font(.system(size: 13, weight: .bold, design: .monospaced)) + .foregroundStyle(Color.textPrimary) + + Button { + scrollSpeed = min(3.0, scrollSpeed + 0.25) + } label: { + Image(systemName: "plus") + .font(.caption.weight(.bold)) + .foregroundStyle(Color.accentWarm) + .frame(width: 32, height: 32) + } + + Divider() + .frame(height: 20) + + Button { + withAnimation(.spring(duration: 0.3)) { + isAutoScrolling = false + } + } label: { + Image(systemName: "stop.fill") + .font(.system(size: 12)) + .foregroundStyle(.white) + .frame(width: 34, height: 34) + .background(Color.red.opacity(0.8)) + .clipShape(Circle()) + } + } + .padding(.horizontal, 14) + .padding(.vertical, 8) + .background(.ultraThinMaterial, in: Capsule()) + .shadow(color: .black.opacity(0.1), radius: 8, y: 2) + .padding(.bottom, 70) + .transition(.move(edge: .bottom).combined(with: .opacity)) + } + .animation(.spring(duration: 0.3), value: isAutoScrolling) } } .confettiCannon( @@ -104,8 +164,6 @@ struct MainTabView: View { .task { guard showReader else { return } let renderer = ArticleRenderer.shared - // Window is now available — attach WKWebView to force GPU - // compositor launch during startup, not first article tap renderer.attachToWindow() await readerVM.loadInitial() } @@ -117,71 +175,6 @@ struct MainTabView: View { confettiTrigger += 1 UINotificationFeedbackGenerator().notificationOccurred(.success) } - - // MARK: - Reader Auto-Scroll Pill (tab bar level) - - private var readerAutoScrollPill: some View { - Group { - if isAutoScrolling { - // Expanded: speed + stop - HStack(spacing: 8) { - Button { - scrollSpeed = max(0.25, scrollSpeed - 0.25) - } label: { - Image(systemName: "minus") - .font(.caption2.weight(.bold)) - .foregroundStyle(Color.accentWarm) - .frame(width: 28, height: 28) - } - - Text(String(format: "%.2fx", scrollSpeed)) - .font(.system(size: 11, weight: .bold, design: .monospaced)) - .foregroundStyle(Color.textPrimary) - - Button { - scrollSpeed = min(3.0, scrollSpeed + 0.25) - } label: { - Image(systemName: "plus") - .font(.caption2.weight(.bold)) - .foregroundStyle(Color.accentWarm) - .frame(width: 28, height: 28) - } - - Button { - withAnimation(.spring(duration: 0.3)) { - isAutoScrolling = false - } - } label: { - Image(systemName: "stop.fill") - .font(.system(size: 11)) - .foregroundStyle(.white) - .frame(width: 32, height: 32) - .background(Color.red.opacity(0.8)) - .clipShape(Circle()) - } - } - .padding(.horizontal, 10) - .padding(.vertical, 6) - .background(.ultraThinMaterial, in: Capsule()) - .transition(.scale(scale: 0.5).combined(with: .opacity)) - } else { - // Collapsed: play button circle (like Photos search icon) - Button { - withAnimation(.spring(duration: 0.3)) { - isAutoScrolling = true - } - } label: { - Image(systemName: "play.fill") - .font(.system(size: 15)) - .foregroundStyle(Color.accentWarm) - .frame(width: 48, height: 48) - .background(.ultraThinMaterial, in: Circle()) - } - .transition(.scale(scale: 0.5).combined(with: .opacity)) - } - } - .animation(.spring(duration: 0.3), value: isAutoScrolling) - } } // MARK: - Assistant Sheet