Reorganize toolbars placement

This commit is contained in:
Arkadiusz Fal 2022-02-04 18:38:29 +01:00
parent 249c7ca7fa
commit 9868a2ef01
9 changed files with 197 additions and 152 deletions

View File

@ -29,11 +29,15 @@ private struct CurrentPlaylistID: EnvironmentKey {
static let defaultValue: String? = nil
}
typealias LoadMoreContentHandlerType = () -> Void
private struct LoadMoreContentHandler: EnvironmentKey {
static let defaultValue: LoadMoreContentHandlerType = {}
}
typealias LoadMoreContentHandlerType = () -> Void
private struct ScrollViewBottomPaddingKey: EnvironmentKey {
static let defaultValue: Double = 30
}
extension EnvironmentValues {
var inNavigationView: Bool {
@ -70,4 +74,9 @@ extension EnvironmentValues {
get { self[LoadMoreContentHandler.self] }
set { self[LoadMoreContentHandler.self] = newValue }
}
var scrollViewBottomPadding: Double {
get { self[ScrollViewBottomPaddingKey.self] }
set { self[ScrollViewBottomPaddingKey.self] = newValue }
}
}

View File

@ -39,6 +39,7 @@ struct FavoritesView: View {
.padding(.top, item == first && RefreshControl.navigationBarTitleDisplayMode == .inline ? 10 : 0)
#endif
}
Color.clear.padding(.bottom, 30)
#endif
}
}

View File

@ -53,7 +53,43 @@ struct PlaylistsView: View {
}
var body: some View {
PlayerControlsView {
PlayerControlsView(toolbar: {
HStack {
HStack {
newPlaylistButton
.offset(x: -10)
if currentPlaylist != nil {
editPlaylistButton
}
}
if !model.isEmpty {
Spacer()
}
HStack {
if model.isEmpty {
Text("No Playlists")
.foregroundColor(.secondary)
} else {
selectPlaylistButton
.transaction { t in t.animation = .none }
}
}
Spacer()
if currentPlaylist != nil {
HStack(spacing: 0) {
playButton
shuffleButton
}
.offset(x: 10)
}
}
.padding(.horizontal)
}) {
SignInRequiredView(title: "Playlists") {
VStack {
#if os(tvOS)
@ -72,6 +108,7 @@ struct PlaylistsView: View {
Spacer()
#else
VerticalCells(items: items)
.environment(\.scrollViewBottomPadding, 70)
#endif
}
.environment(\.currentPlaylistID, currentPlaylist?.id)
@ -79,6 +116,18 @@ struct PlaylistsView: View {
}
}
}
.onAppear {
model.load()
}
.onChange(of: accounts.current) { _ in
model.load(force: true)
}
.onChange(of: selectedPlaylistID) { _ in
resource?.load()
}
.onChange(of: model.reloadPlaylists) { _ in
resource?.load()
}
#if os(tvOS)
.fullScreenCover(isPresented: $showingNewPlaylist, onDismiss: selectCreatedPlaylist) {
PlaylistFormView(playlist: $createdPlaylist)
@ -88,74 +137,25 @@ struct PlaylistsView: View {
PlaylistFormView(playlist: $editedPlaylist)
.environmentObject(accounts)
}
.focusScope(focusNamespace)
#else
.background(
EmptyView()
.sheet(isPresented: $showingNewPlaylist, onDismiss: selectCreatedPlaylist) {
PlaylistFormView(playlist: $createdPlaylist)
.environmentObject(accounts)
}
)
.background(
EmptyView()
.sheet(isPresented: $showingEditPlaylist, onDismiss: selectEditedPlaylist) {
PlaylistFormView(playlist: $editedPlaylist)
.environmentObject(accounts)
}
)
.background(
EmptyView()
.sheet(isPresented: $showingNewPlaylist, onDismiss: selectCreatedPlaylist) {
PlaylistFormView(playlist: $createdPlaylist)
.environmentObject(accounts)
}
)
.background(
EmptyView()
.sheet(isPresented: $showingEditPlaylist, onDismiss: selectEditedPlaylist) {
PlaylistFormView(playlist: $editedPlaylist)
.environmentObject(accounts)
}
)
#endif
.toolbar {
#if os(iOS)
ToolbarItemGroup(placement: .bottomBar) {
Group {
if model.isEmpty {
Text("No Playlists")
.foregroundColor(.secondary)
} else {
selectPlaylistButton
.transaction { t in t.animation = .none }
}
Spacer()
if currentPlaylist != nil {
HStack(spacing: 10) {
playButton
shuffleButton
}
Spacer()
}
HStack(spacing: 2) {
newPlaylistButton
if currentPlaylist != nil {
editPlaylistButton
}
}
}
}
#endif
}
#if os(tvOS)
.focusScope(focusNamespace)
#endif
.onAppear {
model.load()
resource?.load()
}
.onChange(of: accounts.current) { _ in
model.load(force: true)
}
.onChange(of: selectedPlaylistID) { _ in
resource?.load()
}
.onChange(of: model.reloadPlaylists) { _ in
resource?.load()
}
#if os(iOS)
.navigationBarTitleDisplayMode(RefreshControl.navigationBarTitleDisplayMode)
.navigationBarTitleDisplayMode(RefreshControl.navigationBarTitleDisplayMode)
#endif
}
@ -261,7 +261,7 @@ struct PlaylistsView: View {
}
} label: {
Text(currentPlaylist?.title ?? "Select playlist")
.frame(maxWidth: 140, alignment: .leading)
.frame(maxWidth: 140, alignment: .center)
}
#endif
}
@ -272,16 +272,17 @@ struct PlaylistsView: View {
self.showingEditPlaylist = true
}) {
HStack(spacing: 8) {
Image(systemName: "slider.horizontal.3")
Text("Edit")
Image(systemName: "rectangle.and.pencil.and.ellipsis")
}
}
}
var newPlaylistButton: some View {
Button(action: { self.showingNewPlaylist = true }) {
HStack(spacing: 8) {
HStack(spacing: 0) {
Image(systemName: "plus")
.padding(8)
.contentShape(Rectangle())
#if os(tvOS)
Text("New Playlist")
#endif
@ -294,6 +295,8 @@ struct PlaylistsView: View {
player.play(items.compactMap(\.video))
} label: {
Image(systemName: "play")
.padding(8)
.contentShape(Rectangle())
}
}
@ -302,6 +305,8 @@ struct PlaylistsView: View {
player.play(items.compactMap(\.video), shuffling: true)
} label: {
Image(systemName: "shuffle")
.padding(8)
.contentShape(Rectangle())
}
}

View File

@ -41,7 +41,23 @@ struct SearchView: View {
}
var body: some View {
PlayerControlsView {
PlayerControlsView(toolbar: {
#if os(iOS)
if accounts.app.supportsSearchFilters {
HStack(spacing: 0) {
Menu("Sort: \(searchSortOrder.name)") {
searchSortOrderPicker
}
.transaction { t in t.animation = .none }
Spacer()
filtersMenu
}
.padding()
}
#endif
}) {
#if os(iOS)
VStack {
SearchTextField(favoriteItem: $favoriteItem)
@ -70,27 +86,19 @@ struct SearchView: View {
#endif
}
.toolbar {
#if !os(tvOS)
#if os(macOS)
ToolbarItemGroup(placement: toolbarPlacement) {
#if os(macOS)
FavoriteButton(item: favoriteItem)
.id(favoriteItem?.id)
#endif
FavoriteButton(item: favoriteItem)
.id(favoriteItem?.id)
if accounts.app.supportsSearchFilters {
Section {
#if os(macOS)
HStack {
Text("Sort:")
.foregroundColor(.secondary)
HStack {
Text("Sort:")
.foregroundColor(.secondary)
searchSortOrderPicker
}
#else
Menu("Sort: \(searchSortOrder.name)") {
searchSortOrderPicker
}
#endif
searchSortOrderPicker
}
}
.transaction { t in t.animation = .none }
}
@ -99,9 +107,7 @@ struct SearchView: View {
filtersMenu
}
#if os(macOS)
SearchTextField()
#endif
SearchTextField()
}
#endif
}

View File

@ -33,7 +33,39 @@ struct TrendingView: View {
}
var body: some View {
PlayerControlsView {
PlayerControlsView(toolbar: {
HStack {
if accounts.app.supportsTrendingCategories {
HStack {
Text("Category")
.foregroundColor(.secondary)
categoryButton
// only way to disable Menu animation is to
// force redraw of the view when it changes
.id(UUID())
}
Spacer()
}
if let favoriteItem = favoriteItem {
FavoriteButton(item: favoriteItem, labelPadding: true)
.id(favoriteItem.id)
.labelStyle(.iconOnly)
Spacer()
}
HStack {
Text("Country")
.foregroundColor(.secondary)
countryButton
}
}
.padding(.horizontal)
}) {
Section {
VStack(alignment: .center, spacing: 0) {
#if os(tvOS)
@ -44,6 +76,7 @@ struct TrendingView: View {
Spacer()
#else
VerticalCells(items: trending)
.environment(\.scrollViewBottomPadding, 70)
#endif
}
}
@ -62,38 +95,6 @@ struct TrendingView: View {
}
countryButton
}
#elseif os(iOS)
ToolbarItemGroup(placement: .bottomBar) {
Group {
if accounts.app.supportsTrendingCategories {
HStack {
Text("Category")
.foregroundColor(.secondary)
categoryButton
// only way to disable Menu animation is to
// force redraw of the view when it changes
.id(UUID())
}
Spacer()
}
if let favoriteItem = favoriteItem {
FavoriteButton(item: favoriteItem)
.id(favoriteItem.id)
Spacer()
}
HStack {
Text("Country")
.foregroundColor(.secondary)
countryButton
}
}
}
#endif
}
.onChange(of: resource) { _ in

View File

@ -6,6 +6,7 @@ struct VerticalCells: View {
@Environment(\.verticalSizeClass) private var verticalSizeClass
#endif
@Environment(\.scrollViewBottomPadding) private var scrollViewBottomPadding
@Environment(\.loadMoreContentHandler) private var loadMoreContentHandler
var items = [ContentItem]()
@ -20,6 +21,9 @@ struct VerticalCells: View {
}
}
.padding()
#if !os(tvOS)
Color.clear.padding(.bottom, scrollViewBottomPadding)
#endif
}
.edgesIgnoringSafeArea(.horizontal)
#if os(macOS)

View File

@ -5,6 +5,12 @@ import SwiftUI
struct FavoriteButton: View {
let item: FavoriteItem!
let favorites = FavoritesModel.shared
let labelPadding: Bool
init(item: FavoriteItem?, labelPadding: Bool = false) {
self.item = item
self.labelPadding = labelPadding
}
@State private var isFavorite = false
@ -19,11 +25,17 @@ struct FavoriteButton: View {
favorites.toggle(item)
isFavorite.toggle()
} label: {
if isFavorite {
Label("Remove from Favorites", systemImage: "heart.fill")
} else {
Label("Add to Favorites", systemImage: "heart")
Group {
if isFavorite {
Label("Remove from Favorites", systemImage: "heart.fill")
} else {
Label("Add to Favorites", systemImage: "heart")
}
}
#if os(iOS)
.padding(labelPadding ? 10 : 0)
.contentShape(Rectangle())
#endif
}
.disabled(item.isNil)
.onAppear {

View File

@ -1,14 +1,20 @@
import Foundation
import SwiftUI
struct PlayerControlsView<Content: View>: View {
struct PlayerControlsView<Content: View, Toolbar: View>: View {
let content: Content
let toolbar: Toolbar?
@Environment(\.navigationStyle) private var navigationStyle
@EnvironmentObject<PlayerModel> private var model
init(@ViewBuilder content: @escaping () -> Content) {
init(@ViewBuilder toolbar: @escaping () -> Toolbar? = { nil }, @ViewBuilder content: @escaping () -> Content) {
self.content = content()
self.toolbar = toolbar()
}
init(@ViewBuilder content: @escaping () -> Content) where Toolbar == EmptyView {
self.init(toolbar: { EmptyView() }, content: content)
}
var body: some View {
@ -16,17 +22,30 @@ struct PlayerControlsView<Content: View>: View {
content
#if !os(tvOS)
.frame(minHeight: 0, maxHeight: .infinity)
.padding(.bottom, 50)
#endif
#if !os(tvOS)
controls
Group {
#if !os(tvOS)
#if !os(macOS)
toolbar
.frame(height: 100)
.offset(x: 0, y: -28)
#endif
controls
#endif
}
.borderTop(height: 0.4, color: Color("ControlsBorderColor"))
#if os(macOS)
.background(VisualEffectBlur(material: .sidebar))
#elseif os(iOS)
.background(VisualEffectBlur(blurStyle: .systemThinMaterial).edgesIgnoringSafeArea(.all))
#endif
}
}
private var controls: some View {
let controls = HStack {
HStack {
Button(action: {
model.togglePlayer()
}) {
@ -57,6 +76,7 @@ struct PlayerControlsView<Content: View>: View {
Spacer()
}
.padding(.vertical)
.contentShape(Rectangle())
}
.padding(.vertical, 20)
@ -84,6 +104,8 @@ struct PlayerControlsView<Content: View>: View {
Button(action: { model.advanceToNextItem() }) {
Label("Next", systemImage: "forward.fill")
.padding(.vertical)
.contentShape(Rectangle())
}
.disabled(model.queue.isEmpty)
}
@ -91,10 +113,9 @@ struct PlayerControlsView<Content: View>: View {
ProgressView(value: progressViewValue, total: progressViewTotal)
.progressViewStyle(.linear)
#if os(iOS)
.offset(x: 0, y: 8)
.frame(maxWidth: 60)
#else
.offset(x: 0, y: 15)
.offset(y: 6)
.frame(maxWidth: 70)
#endif
}
@ -111,20 +132,6 @@ struct PlayerControlsView<Content: View>: View {
model.show()
})
#endif
return Group {
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) {
controls
.background(Material.ultraThinMaterial)
} else {
controls
#if os(macOS)
.background(VisualEffectBlur(material: .hudWindow))
#elseif os(iOS)
.background(VisualEffectBlur(blurStyle: .systemUltraThinMaterial))
#endif
}
}
}
private var progressViewValue: Double {

View File

@ -195,7 +195,7 @@ struct VideoContextMenuView: View {
Button {
navigation.presentAddToPlaylist(video)
} label: {
Label("Add to playlist...", systemImage: "text.badge.plus")
Label("Add to Playlist...", systemImage: "text.badge.plus")
}
}
@ -203,7 +203,7 @@ struct VideoContextMenuView: View {
Button {
playlists.removeVideo(index: video.indexID!, playlistID: playlistID)
} label: {
Label("Remove from playlist", systemImage: "text.badge.minus")
Label("Remove from Playlist", systemImage: "text.badge.minus")
}
}
}