From e2fc87b6aaedf187f8d35ed92dd87d28e35729f7 Mon Sep 17 00:00:00 2001 From: Yusuf Suleman Date: Sat, 4 Apr 2026 07:55:50 -0500 Subject: [PATCH] feat: Tab(role: .search) with context-dependent action per tab Single separated circle in tab bar (like Photos search icon): - Home/Fitness: shows + icon, taps opens food assistant - Reader idle: shows play icon, taps starts auto-scroll - Reader playing: shows pause icon, taps stops auto-scroll Icon updates dynamically via computed actionIcon property. handleActionTap() routes the tap based on selectedTab. After action, selectedTab returns to the previous tab (doesn't stay on the invisible "action" tab). Speed controls still appear as glass capsule overlay when playing. Co-Authored-By: Claude Opus 4.6 (1M context) --- ios/Platform/Platform/ContentView.swift | 86 +++++++++++++------------ 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/ios/Platform/Platform/ContentView.swift b/ios/Platform/Platform/ContentView.swift index f4c0a2a..efa170f 100644 --- a/ios/Platform/Platform/ContentView.swift +++ b/ios/Platform/Platform/ContentView.swift @@ -35,6 +35,14 @@ struct MainTabView: View { auth.currentUser?.id != 4 } + // Dynamic icon for the search-role tab based on context + private var actionIcon: String { + if selectedTab == 2 { + return isAutoScrolling ? "pause.fill" : "play.fill" + } + return "plus" + } + var body: some View { ZStack { TabView(selection: $selectedTab) { @@ -51,55 +59,37 @@ struct MainTabView: View { ReaderTabView(vm: readerVM, isAutoScrolling: $isAutoScrolling, scrollSpeed: $scrollSpeed) } } + + // Action button — separated circle on trailing side of tab bar + // Home/Fitness: quick add food (+) + // Reader: play/pause auto-scroll + Tab(value: 3, role: .search) { + // This view shows briefly when tapped — immediately redirect + Color.clear + .onAppear { + handleActionTap() + } + } label: { + Label("Action", systemImage: actionIcon) + } } .tint(Color.accentWarm) .tabBarMinimizeBehavior(.onScrollDown) - // Floating action button — context-dependent - VStack { - Spacer() - HStack(alignment: .bottom) { - if selectedTab != 2 { + // Feedback button (not on Reader) + if selectedTab != 2 { + VStack { + Spacer() + HStack { FeedbackButton() .padding(.leading, 20) + Spacer() } - - Spacer() - - if selectedTab == 2 && showReader { - // Reader: play/pause auto-scroll - Button { - withAnimation(.spring(duration: 0.3)) { - isAutoScrolling.toggle() - } - } label: { - Image(systemName: isAutoScrolling ? "pause.fill" : "play.fill") - .font(.system(size: 18)) - .foregroundStyle(.white) - .frame(width: 56, height: 56) - .background(isAutoScrolling ? Color.red.opacity(0.85) : Color.accentWarm) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.2), radius: 8, y: 4) - } - .padding(.trailing, 20) - } else { - // Home/Fitness: FAB (+) for food - 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) } - .padding(.bottom, 70) } - // Auto-scroll speed controls (overlay when playing) + // Auto-scroll speed controls (overlay when playing on Reader) if isAutoScrolling && selectedTab == 2 { VStack { Spacer() @@ -130,7 +120,7 @@ struct MainTabView: View { .padding(.vertical, 8) .background(.ultraThinMaterial, in: Capsule()) .shadow(color: .black.opacity(0.1), radius: 8, y: 2) - .padding(.bottom, 140) + .padding(.bottom, 90) .transition(.move(edge: .bottom).combined(with: .opacity)) } .animation(.spring(duration: 0.3), value: isAutoScrolling) @@ -157,11 +147,25 @@ struct MainTabView: View { await readerVM.loadInitial() } .onChange(of: selectedTab) { _, newTab in - // Stop auto-scroll when leaving Reader if newTab != 2 { isAutoScrolling = false } } } + private func handleActionTap() { + if selectedTab == 2 { + // Reader: toggle auto-scroll, stay on Reader + withAnimation(.spring(duration: 0.3)) { + isAutoScrolling.toggle() + } + selectedTab = 2 // stay on Reader (don't switch to the "action" tab) + } else { + // Home/Fitness: open food assistant, return to previous tab + let returnTab = selectedTab + showAssistant = true + selectedTab = returnTab + } + } + private func foodAdded() { showAssistant = false selectedTab = 1