Files
yattee/Shared/Home/HomeView.swift
Arkadiusz Fal 9177abb0ec Fix iOS menu text disappearing in navigation headers
Extended the ZStack overlay fix to all iOS navigation header menus
where text labels would disappear when tapping the menu:

- HomeView: "Home" title menu
- PopularView: "Popular" title with icon menu
- TrendingView: Country/flag title menu
- PlaylistsView: Playlist title with thumbnail menu
- ChannelPlaylistView: Playlist title with thumbnail menu
- OpenVideosView: Playback mode picker menu

All menus now use the same pattern as PlaybackSettings:
- Visible static label layer in ZStack
- Invisible Menu overlay with .opacity(0)
- Prevents text disappearing and resizing animations
2025-11-23 14:16:21 +01:00

268 lines
9.4 KiB
Swift

import Defaults
import Siesta
import SwiftUI
import UniformTypeIdentifiers
struct HomeView: View {
@ObservedObject private var accounts = AccountsModel.shared
@ObservedObject private var player = PlayerModel.shared
@State private var presentingHomeSettings = false
@State private var favoritesChanged = false
@State private var updateTask: Task<Void, Never>?
@FetchRequest(sortDescriptors: [.init(key: "watchedAt", ascending: false)])
var watches: FetchedResults<Watch>
@State private var historyID = UUID()
#if os(iOS)
@State private var recentDocumentsID = UUID()
#endif
#if !os(tvOS)
@Default(.favorites) private var favorites
@Default(.widgetsSettings) private var widgetsSettings
#endif
@Default(.showFavoritesInHome) private var showFavoritesInHome
@Default(.showOpenActionsInHome) private var showOpenActionsInHome
@Default(.showQueueInHome) private var showQueueInHome
private var navigation: NavigationModel { .shared }
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
LazyVStack(spacing: 0, pinnedViews: []) {
VStack {
#if !os(tvOS)
HStack {
if showOpenActionsInHome {
AccentButton(text: "Files", imageSystemName: "folder") {
NavigationModel.shared.presentingFileImporter = true
}
AccentButton(text: "Paste", imageSystemName: "doc.on.clipboard.fill") {
OpenVideosModel.shared.openURLsFromClipboard(playbackMode: .playNow)
}
AccentButton(imageSystemName: "ellipsis") {
NavigationModel.shared.presentingOpenVideos = true
}
.frame(maxWidth: 40)
}
}
#endif
#if os(tvOS)
HStack {
if showOpenActionsInHome {
Button {
NavigationModel.shared.presentingOpenVideos = true
} label: {
Label("Open Video", systemImage: "globe")
}
}
Button {
NavigationModel.shared.presentingAccounts = true
} label: {
Label("Locations", systemImage: "globe")
}
Spacer()
HideWatchedButtons()
HideShortsButtons()
Button {
NavigationModel.shared.presentingSettings = true
} label: {
Label("Settings", systemImage: "gear")
}
}
.font(.caption)
.imageScale(.small)
.foregroundColor(.primary)
#endif
}
.padding(.top, 15)
.padding(.bottom, 15)
#if os(tvOS)
.padding(.horizontal, 40)
#else
.padding(.horizontal, 15)
#endif
if showQueueInHome {
QueueView()
#if os(tvOS)
.padding(.horizontal, 40)
#else
.padding(.horizontal, 15)
#endif
}
if !accounts.current.isNil, showFavoritesInHome {
#if os(tvOS)
ForEach(Defaults[.favorites]) { item in
FavoriteItemView(item: item, favoritesChanged: $favoritesChanged)
.animation(nil, value: favoritesChanged)
}
#else
ForEach(favorites) { item in
FavoriteItemView(item: item, favoritesChanged: $favoritesChanged)
.animation(nil, value: favoritesChanged)
#if os(macOS)
.workaroundForVerticalScrollingBug()
#endif
}
#endif
}
#if !os(tvOS)
Color.clear.padding(.bottom, 60)
#endif
}
}
.animation(nil, value: favoritesChanged)
.onAppear {
updateTask = Task {
async let favoritesUpdates: Void = {
for await _ in Defaults.updates(.favorites) {
await MainActor.run {
favoritesChanged.toggle()
}
}
}()
async let widgetsUpdates: Void = {
for await _ in Defaults.updates(.widgetsSettings) {
await MainActor.run {
favoritesChanged.toggle()
}
}
}()
_ = await(favoritesUpdates, widgetsUpdates)
}
}
.onDisappear {
updateTask?.cancel()
}
.onChange(of: player.presentingPlayer) { presenting in
if presenting {
updateTask?.cancel()
} else {
updateTask = Task {
async let favoritesUpdates: Void = {
for await _ in Defaults.updates(.favorites) {
await MainActor.run {
favoritesChanged.toggle()
}
}
}()
async let widgetsUpdates: Void = {
for await _ in Defaults.updates(.widgetsSettings) {
await MainActor.run {
favoritesChanged.toggle()
}
}
}()
_ = await(favoritesUpdates, widgetsUpdates)
}
}
}
.redrawOn(change: favoritesChanged)
#if os(tvOS)
.edgesIgnoringSafeArea(.horizontal)
#else
.navigationTitle("Home")
#endif
#if os(macOS)
.background(Color.secondaryBackground)
.frame(minWidth: Constants.contentViewMinWidth)
.toolbar {
ToolbarItemGroup(placement: .automatic) {
HideWatchedButtons()
HideShortsButtons()
HomeSettingsButton()
}
}
#endif
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
homeMenu
}
}
#endif
#if !os(macOS)
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
favoritesChanged.toggle()
}
#endif
}
func sectionLabel(_ label: String) -> some View {
Text(label.localized())
#if os(tvOS)
.padding(.horizontal, 40)
#else
.padding(.horizontal, 15)
#endif
.font(.title3.bold())
.frame(maxWidth: .infinity, alignment: .leading)
.foregroundColor(.secondary)
}
#if os(iOS)
var homeMenu: some View {
ZStack {
HStack(spacing: 12) {
Text("Home")
.foregroundColor(.primary)
.font(.headline)
Image(systemName: "chevron.down.circle.fill")
.foregroundColor(.accentColor)
.imageScale(.small)
}
Menu {
Section {
HideWatchedButtons()
HideShortsButtons()
}
Section {
Button {
navigation.presentingHomeSettings = true
} label: {
Label("Home Settings", systemImage: "gear")
}
}
} label: {
HStack(spacing: 12) {
Text("Home")
.foregroundColor(.primary)
.font(.headline)
Image(systemName: "chevron.down.circle.fill")
.foregroundColor(.accentColor)
.imageScale(.small)
}
.opacity(0)
}
}
.transaction { t in t.animation = nil }
}
#endif
}
struct Home_Previews: PreviewProvider {
static var previews: some View {
TabView {
NavigationView {
HomeView()
.injectFixtureEnvironmentObjects()
.tabItem {
Label("Home", systemImage: "house")
}
}
}
}
}