Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a configuration to allow the display of the Toast above the mini-player #2555

Open
wants to merge 4 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions podcasts/Common SwiftUI/Toast/Toast.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import Foundation

protocol ToastInsetAdjuster {
var bottomInset: CGFloat { get }
}

/// 🍞 Toast - A lightweight way to display informative overlay messages
///
/// Usage:
Expand All @@ -19,16 +23,18 @@ class Toast {
private var window: UIWindow? = nil

/// Display the toast message with the given title and actions
static func show<Style: ToastTheme>(_ title: String, actions: [Action]? = nil, dismissAfter: TimeInterval = 5.0, theme: Style = .defaultTheme) {
static func show<Style: ToastTheme>(_ title: String, actions: [Action]? = nil, dismissAfter: TimeInterval = 5.0, theme: Style = .defaultTheme, insetAdjuster: ToastInsetAdjuster? = nil) {
// Hide any active toasts
shared.toastDismissed()

guard let scene = SceneHelper.connectedScene() else { return }

let viewModel = ToastViewModel(coordinator: shared, title: title, actions: actions, dismissTime: dismissAfter)
let viewModel = ToastViewModel(coordinator: shared, title: title, actions: actions, dismissTime: dismissAfter, insetAdjuster: insetAdjuster)
let view = ToastView(viewModel: viewModel, style: theme)
let controller = ThemedHostingController(rootView: view)

if insetAdjuster == nil {
viewModel.insetAdjuster = controller
}
let window = ToastWindow(windowScene: scene, viewModel: viewModel, controller: controller)
window.makeKeyAndVisible()

Expand All @@ -49,6 +55,12 @@ class Toast {
}
}

extension ThemedHostingController: ToastInsetAdjuster {
var bottomInset: CGFloat {
return view.safeAreaInsets.bottom
}

}
// MARK: - ToastCoordinator

extension Toast: ToastDelegate {
Expand Down
9 changes: 6 additions & 3 deletions podcasts/Common SwiftUI/Toast/ToastView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ struct ToastView<Style: ToastTheme>: View {

autoDismiss()
}
.ignoresSafeArea()
}

// MARK: - Views
Expand Down Expand Up @@ -109,15 +110,17 @@ struct ToastView<Style: ToastTheme>: View {
.shadow(color: .black.opacity(0.3), radius: 10)

// Animates the toast in from the bottom of the screen
.offset(y: isVisible ? 0 : contentSize.height)
.offset(y: -(viewModel.insetAdjuster?.bottomInset ?? 0) + (isVisible ? 0 : contentSize.height))
.opacity(isVisible ? 1 : 0)
.animation(ToastConstants.animation, value: isVisible)

// Calculate the view size and inform the view model
.background(GeometryReader(content: { proxy in
Color.clear.onAppear {
contentSize = proxy.size
viewModel.updateFrame(proxy.frame(in: .global))
var rect = proxy.frame(in: .global)
rect.size.height = rect.size.height + (viewModel.insetAdjuster?.bottomInset ?? 0)
rect.origin.y = rect.origin.y - (viewModel.insetAdjuster?.bottomInset ?? 0)
viewModel.updateFrame(rect)
}
}))

Expand Down
4 changes: 3 additions & 1 deletion podcasts/Common SwiftUI/Toast/ToastViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class ToastViewModel: ObservableObject {
let title: String
let actions: [Toast.Action]
let dismissTime: TimeInterval
var insetAdjuster: ToastInsetAdjuster?

deinit {
autoDismissTimer?.invalidate()
Expand All @@ -23,11 +24,12 @@ class ToastViewModel: ObservableObject {
/// When this is true the view should animate out and call `didDismiss`
@Published var didAutoDismiss = false

init(coordinator: ToastDelegate, title: String, actions: [Toast.Action]?, dismissTime: TimeInterval) {
init(coordinator: ToastDelegate, title: String, actions: [Toast.Action]?, dismissTime: TimeInterval, insetAdjuster: ToastInsetAdjuster? = nil) {
self.coordinator = coordinator
self.title = title
self.actions = actions ?? []
self.dismissTime = dismissTime
self.insetAdjuster = insetAdjuster
}

// MARK: - Window Methods
Expand Down
1 change: 1 addition & 0 deletions podcasts/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ struct Constants {
static let sideBarWidthCompact = 88 as CGFloat
static let sideBarWidthExpanded = 320 as CGFloat

static let miniPlayerHeight = 70 as CGFloat
static let miniPlayerOffset = 80 as CGFloat
static let extraShowNotesVerticalSpacing: CGFloat = 60
static let defaultFilterDownloadLimit = 10 as Int32
Expand Down
2 changes: 1 addition & 1 deletion podcasts/MainTabBarController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ class MainTabBarController: UITabBarController, NavigationProtocol {
miniPlayer.view.bottomAnchor.constraint(equalTo: tabBar.topAnchor)
])

miniPlayer.changeHeightTo(miniPlayer.desiredHeight())
miniPlayer.changeHeightTo(miniPlayer.desiredHeight)
}

// MARK: - UITabBarDelegate
Expand Down
4 changes: 2 additions & 2 deletions podcasts/MiniPlayerViewController+Positioning.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ extension MiniPlayerViewController {
// only show if something is playing
if PlaybackManager.shared.currentEpisode() == nil { return }

changeHeightTo(desiredHeight())
changeHeightTo(desiredHeight)
moveToHiddenBottomPosition()
self.view.isHidden = false
view.superview?.layoutIfNeeded()
Expand Down Expand Up @@ -84,7 +84,7 @@ extension MiniPlayerViewController {
}

private func moveToHiddenBottomPosition() {
view.transform = CGAffineTransform(translationX: 0, y: desiredHeight())
view.transform = CGAffineTransform(translationX: 0, y: desiredHeight)
view.superview?.layoutIfNeeded()
}

Expand Down
4 changes: 2 additions & 2 deletions podcasts/MiniPlayerViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ class MiniPlayerViewController: SimpleNotificationsViewController {
PlaybackManager.shared.skipForward()
}

func desiredHeight() -> CGFloat {
70
var desiredHeight: CGFloat {
Constants.Values.miniPlayerHeight
}

func aboutToDisplayFullScreenPlayer() {
Expand Down
11 changes: 8 additions & 3 deletions podcasts/SwiftUI/MiniPlayerPaddingModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@ import SwiftUI
/// Apply a bottom padding whenever the mini player is visible
public struct MiniPlayerSafeAreaInset: ViewModifier {
@State var isMiniPlayerVisible: Bool = false
let multipler: CGFloat

init(multipler: CGFloat) {
self.multipler = multipler
}

public func body(content: Content) -> some View {
content
.safeAreaInset(edge: .bottom, spacing: 0) {
Color.clear.frame(height: Constants.Values.miniPlayerOffset) // Adjust the bottom inset
Color.clear.frame(height: Constants.Values.miniPlayerOffset * multipler) // Adjust the bottom inset
}
.onAppear {
isMiniPlayerVisible = (PlaybackManager.shared.currentEpisode() != nil)
Expand All @@ -24,7 +29,7 @@ public struct MiniPlayerSafeAreaInset: ViewModifier {

// Create an extension for easier usage
public extension View {
func miniPlayerSafeAreaInset() -> some View {
self.modifier(MiniPlayerSafeAreaInset())
func miniPlayerSafeAreaInset(multiplier: CGFloat = 1) -> some View {
self.modifier(MiniPlayerSafeAreaInset(multipler: multiplier))
}
}
12 changes: 11 additions & 1 deletion podcasts/UpNextViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ class UpNextViewController: UIViewController, UIGestureRecognizerDelegate {
}
let upNextShuffleEnabled = Settings.upNextShuffleEnabled()
if upNextShuffleEnabled {
Toast.show(L10n.upNextShuffleToastMessage)
Toast.show(L10n.upNextShuffleToastMessage, insetAdjuster: self)
}
track(.upNextShuffleEnabled, properties: ["value": upNextShuffleEnabled])
}
Expand Down Expand Up @@ -430,3 +430,13 @@ extension UpNextViewController: AnalyticsSourceProvider {
.upNext
}
}

extension UpNextViewController: ToastInsetAdjuster {
var bottomInset: CGFloat {
let isMiniPlayerVisible = PlaybackManager.shared.currentEpisode() != nil
let value = view.safeAreaInsets.bottom + (isMiniPlayerVisible && showingInTab ? Constants.Values.miniPlayerHeight : 0)
return value
}


}