From a88a02b6710f3b95ce2b78a96ec825b5f23adf82 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Sat, 31 Jan 2026 12:01:12 -0600 Subject: [PATCH] Signed-off-by: Matt Bruce --- .../Features/Clock/Views/ClockView.swift | 70 ++++++++++++++----- .../Clock/Views/Components/ClockToolbar.swift | 18 +---- 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/TheNoiseClock/Features/Clock/Views/ClockView.swift b/TheNoiseClock/Features/Clock/Views/ClockView.swift index 5848ccf..3f6c2dc 100644 --- a/TheNoiseClock/Features/Clock/Views/ClockView.swift +++ b/TheNoiseClock/Features/Clock/Views/ClockView.swift @@ -14,6 +14,8 @@ struct ClockView: View { // MARK: - Properties @Bindable var viewModel: ClockViewModel @State private var showFullScreenHint = false + @State private var idleTimer: Timer? + @State private var didHandleTouch = false // MARK: - Body var body: some View { @@ -45,32 +47,66 @@ struct ClockView: View { .overlay { // Toolbar overlay ClockToolbar( - isDisplayMode: viewModel.isDisplayMode, - onFullScreenTap: { - if !viewModel.isDisplayMode { - viewModel.toggleDisplayMode() - - // Show hint after a brief delay to allow transition - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - showFullScreenHint = true - } - } else { - viewModel.toggleDisplayMode() - } - } + isDisplayMode: viewModel.isDisplayMode ) } .overlay { // Tab bar management overlay ClockTabBarManager(isDisplayMode: viewModel.isDisplayMode) } - .overlay { - // Gesture handling overlay - ClockGestureHandler { - viewModel.toggleDisplayMode() + .simultaneousGesture( + DragGesture(minimumDistance: 0) + .onChanged { _ in + guard !didHandleTouch else { return } + didHandleTouch = true + handleUserInteraction() + } + .onEnded { _ in + didHandleTouch = false + } + ) + .onAppear { + resetIdleTimer() + } + .onDisappear { + idleTimer?.invalidate() + idleTimer = nil + } + .onChange(of: viewModel.isDisplayMode) { _, isDisplayMode in + if isDisplayMode { + idleTimer?.invalidate() + idleTimer = nil + } else { + resetIdleTimer() } } } + + // MARK: - Idle Timer + private func resetIdleTimer() { + idleTimer?.invalidate() + idleTimer = nil + guard !viewModel.isDisplayMode else { return } + idleTimer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { _ in + enterDisplayModeFromIdle() + } + } + + private func enterDisplayModeFromIdle() { + guard !viewModel.isDisplayMode else { return } + viewModel.toggleDisplayMode() + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + showFullScreenHint = true + } + } + + private func handleUserInteraction() { + if viewModel.isDisplayMode { + viewModel.toggleDisplayMode() + showFullScreenHint = false + } + resetIdleTimer() + } } // MARK: - Preview diff --git a/TheNoiseClock/Features/Clock/Views/Components/ClockToolbar.swift b/TheNoiseClock/Features/Clock/Views/Components/ClockToolbar.swift index e3ad805..bc1692c 100644 --- a/TheNoiseClock/Features/Clock/Views/Components/ClockToolbar.swift +++ b/TheNoiseClock/Features/Clock/Views/Components/ClockToolbar.swift @@ -12,26 +12,11 @@ struct ClockToolbar: View { // MARK: - Properties let isDisplayMode: Bool - let onFullScreenTap: () -> Void // MARK: - Body var body: some View { EmptyView() .navigationTitle(isDisplayMode ? "" : "Clock") - .toolbar { - if !isDisplayMode { - ToolbarItemGroup(placement: .navigationBarTrailing) { - Button { - onFullScreenTap() - } label: { - Image(systemName: "rectangle.expand.diagonal") - .font(.title2) - .transition(.opacity) - } - .accessibilityLabel("Enter Full Screen Mode") - } - } - } .navigationBarBackButtonHidden(isDisplayMode) .toolbar(isDisplayMode ? .hidden : .automatic) } @@ -41,8 +26,7 @@ struct ClockToolbar: View { #Preview { NavigationStack { ClockToolbar( - isDisplayMode: false, - onFullScreenTap: {} + isDisplayMode: false ) } }