fix: context-dependent FAB — play/pause on Reader, + on other tabs
All checks were successful
Security Checks / dependency-audit (push) Successful in 13s
Security Checks / secret-scanning (push) Successful in 4s
Security Checks / dockerfile-lint (push) Successful in 4s

Removed Tab(role: .search) — it was always visible on all tabs.
Now the floating action button changes based on selected tab:

- Home/Fitness: FAB (+) → opens food assistant (same as before)
- Reader: Play/Pause circle → toggles auto-scroll
  - Idle: play.fill icon, warm accent background
  - Playing: pause.fill icon, red background
  - Tapping toggles between play/pause

Speed controls appear as glass capsule above the FAB when playing.
Auto-scroll stops when switching away from Reader tab.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yusuf Suleman
2026-04-04 07:41:50 -05:00
parent 1205ac38d0
commit e9373ceac3

View File

@@ -50,35 +50,40 @@ struct MainTabView: View {
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())
.tabBarMinimizeBehavior(.onScrollDown)
// Floating buttons (hidden on Reader tab)
if selectedTab != 2 {
VStack {
Spacer()
HStack(alignment: .bottom) {
// Floating action button context-dependent
VStack {
Spacer()
HStack(alignment: .bottom) {
if selectedTab != 2 {
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))
@@ -90,8 +95,8 @@ struct MainTabView: View {
}
.padding(.trailing, 20)
}
.padding(.bottom, 70)
}
.padding(.bottom, 70)
}
// Auto-scroll speed controls (overlay when playing)
@@ -120,28 +125,12 @@ struct MainTabView: View {
.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)
.padding(.bottom, 140)
.transition(.move(edge: .bottom).combined(with: .opacity))
}
.animation(.spring(duration: 0.3), value: isAutoScrolling)
@@ -167,6 +156,10 @@ struct MainTabView: View {
renderer.attachToWindow()
await readerVM.loadInitial()
}
.onChange(of: selectedTab) { _, newTab in
// Stop auto-scroll when leaving Reader
if newTab != 2 { isAutoScrolling = false }
}
}
private func foodAdded() {
@@ -233,11 +226,3 @@ struct AssistantSheetView: View {
}
}
}
// MARK: - Tab Bar Minimize (iOS 26+)
struct TabBarMinimizeModifier: ViewModifier {
func body(content: Content) -> some View {
content.tabBarMinimizeBehavior(.onScrollDown)
}
}