import Defaults import SwiftUI struct BrowsingSettings: View { #if !os(tvOS) @Default(.accountPickerDisplaysUsername) private var accountPickerDisplaysUsername @Default(.roundedThumbnails) private var roundedThumbnails #endif @Default(.accountPickerDisplaysAnonymousAccounts) private var accountPickerDisplaysAnonymousAccounts @Default(.showUnwatchedFeedBadges) private var showUnwatchedFeedBadges #if os(iOS) @Default(.homeRecentDocumentsItems) private var homeRecentDocumentsItems @Default(.lockPortraitWhenBrowsing) private var lockPortraitWhenBrowsing @Default(.showDocuments) private var showDocuments #endif @Default(.thumbnailsQuality) private var thumbnailsQuality @Default(.channelOnThumbnail) private var channelOnThumbnail @Default(.timeOnThumbnail) private var timeOnThumbnail @Default(.showHome) private var showHome @Default(.showFavoritesInHome) private var showFavoritesInHome @Default(.showQueueInHome) private var showQueueInHome @Default(.showOpenActionsInHome) private var showOpenActionsInHome @Default(.showOpenActionsToolbarItem) private var showOpenActionsToolbarItem @Default(.homeHistoryItems) private var homeHistoryItems @Default(.visibleSections) private var visibleSections @Default(.playerButtonSingleTapGesture) private var playerButtonSingleTapGesture @Default(.playerButtonDoubleTapGesture) private var playerButtonDoubleTapGesture @Default(.playerButtonShowsControlButtonsWhenMinimized) private var playerButtonShowsControlButtonsWhenMinimized @Default(.playerButtonIsExpanded) private var playerButtonIsExpanded @Default(.playerBarMaxWidth) private var playerBarMaxWidth @Default(.expandChannelDescription) private var expandChannelDescription @ObservedObject private var accounts = AccountsModel.shared @State private var homeHistoryItemsText = "" #if os(iOS) @State private var homeRecentDocumentsItemsText = "" #endif #if os(macOS) @State private var presentingEditFavoritesSheet = false #endif var body: some View { Group { #if os(macOS) VStack(alignment: .leading) { sections Spacer() } #else List { sections } #if os(iOS) .listStyle(.insetGrouped) #endif #endif } #if os(tvOS) .frame(maxWidth: 1000) #else .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) #endif .navigationTitle("Browsing") } private var sections: some View { Group { homeSettings let interface = interfaceSettings #if os(tvOS) if !accounts.isEmpty { interface } #else playerBarSettings interface #endif if !accounts.isEmpty { thumbnailsSettings visibleSectionsSettings } } } private var homeSettings: some View { Section(header: SettingsHeader(text: "Home".localized())) { #if !os(tvOS) if !accounts.isEmpty { Toggle("Show Home", isOn: $showHome) } #endif Toggle("Show Open Videos quick actions", isOn: $showOpenActionsInHome) Toggle("Show Next in Queue", isOn: $showQueueInHome) #if os(iOS) HStack { Text("Recent Documents") TextField("Recent Documents", text: $homeRecentDocumentsItemsText) .multilineTextAlignment(.trailing) .labelsHidden() #if !os(macOS) .keyboardType(.numberPad) #endif .onAppear { homeRecentDocumentsItemsText = String(homeRecentDocumentsItems) } .onChange(of: homeRecentDocumentsItemsText) { newValue in homeRecentDocumentsItems = Int(newValue) ?? 3 } } #endif HStack { Text("Recent History") TextField("Recent History", text: $homeHistoryItemsText) .multilineTextAlignment(.trailing) .labelsHidden() #if !os(macOS) .keyboardType(.numberPad) #endif .onAppear { homeHistoryItemsText = String(homeHistoryItems) } .onChange(of: homeHistoryItemsText) { newValue in homeHistoryItems = Int(newValue) ?? 10 } } if !accounts.isEmpty { Toggle("Show Favorites", isOn: $showFavoritesInHome) Group { #if os(macOS) Button { presentingEditFavoritesSheet = true } label: { Text("Edit Favorites…") } .sheet(isPresented: $presentingEditFavoritesSheet) { VStack(alignment: .leading) { Button("Done") { presentingEditFavoritesSheet = false } .padding() .keyboardShortcut(.cancelAction) EditFavorites() } .frame(width: 500, height: 300) } #else NavigationLink(destination: LazyView(EditFavorites())) { Text("Edit Favorites…") } #endif } .disabled(!showFavoritesInHome) } } } #if !os(tvOS) private var playerBarSettings: some View { Section(header: SettingsHeader(text: "Player Bar".localized()), footer: playerBarFooter) { Toggle("Open expanded", isOn: $playerButtonIsExpanded) Toggle("Always show controls buttons", isOn: $playerButtonShowsControlButtonsWhenMinimized) playerBarGesturePicker("Single tap gesture".localized(), selection: $playerButtonSingleTapGesture) playerBarGesturePicker("Double tap gesture".localized(), selection: $playerButtonDoubleTapGesture) HStack { Text("Maximum width expanded") Spacer() TextField("Maximum width expanded", text: $playerBarMaxWidth) .frame(maxWidth: 100, alignment: .trailing) .multilineTextAlignment(.trailing) .labelsHidden() #if !os(macOS) .keyboardType(.numberPad) #endif } } } func playerBarGesturePicker(_ label: String, selection: Binding) -> some View { Picker(label, selection: selection) { ForEach(PlayerTapGestureAction.allCases, id: \.rawValue) { action in Text(action.label.localized()).tag(action) } } } var playerBarFooter: some View { #if os(iOS) Text("Tap and hold channel thumbnail to open context menu with more actions") #elseif os(macOS) Text("Right click channel thumbnail to open context menu with more actions") .foregroundColor(.secondary) .padding(.bottom, 10) #endif } #endif private var interfaceSettings: some View { Section(header: SettingsHeader(text: "Interface".localized())) { #if !os(tvOS) Toggle("Show Open Videos toolbar button", isOn: $showOpenActionsToolbarItem) #endif #if os(iOS) Toggle("Show Documents", isOn: $showDocuments) Toggle("Lock portrait mode", isOn: $lockPortraitWhenBrowsing) .onChange(of: lockPortraitWhenBrowsing) { lock in if lock { Orientation.lockOrientation(.portrait, andRotateTo: .portrait) } else { Orientation.lockOrientation(.allButUpsideDown) } } #endif if !accounts.isEmpty { #if !os(tvOS) Toggle("Show account username", isOn: $accountPickerDisplaysUsername) #endif Toggle("Show anonymous accounts", isOn: $accountPickerDisplaysAnonymousAccounts) Toggle("Show unwatched feed badges", isOn: $showUnwatchedFeedBadges) .onChange(of: showUnwatchedFeedBadges) { newValue in if newValue { FeedModel.shared.calculateUnwatchedFeed() } } } Toggle("Open channels with description expanded", isOn: $expandChannelDescription) } } private var thumbnailsSettings: some View { Section(header: SettingsHeader(text: "Thumbnails".localized())) { thumbnailsQualityPicker #if !os(tvOS) Toggle("Round corners", isOn: $roundedThumbnails) #endif Toggle("Show channel name", isOn: $channelOnThumbnail) Toggle("Show video length", isOn: $timeOnThumbnail) } } private var thumbnailsQualityPicker: some View { Picker("Quality", selection: $thumbnailsQuality) { ForEach(ThumbnailsQuality.allCases, id: \.self) { quality in Text(quality.description) } } .modifier(SettingsPickerModifier()) } private var visibleSectionsSettings: some View { Section(header: SettingsHeader(text: "Sections".localized())) { #if os(macOS) let list = ForEach(VisibleSection.allCases, id: \.self) { section in MultiselectRow( title: section.title, selected: visibleSections.contains(section) ) { value in toggleSection(section, value: value) } } Group { if #available(macOS 12.0, *) { list .listStyle(.inset(alternatesRowBackgrounds: true)) } else { list .listStyle(.inset) } Spacer() } #else ForEach(VisibleSection.allCases, id: \.self) { section in MultiselectRow( title: section.title, selected: visibleSections.contains(section) ) { value in toggleSection(section, value: value) } } #endif } } private func toggleSection(_ section: VisibleSection, value: Bool) { if value { visibleSections.insert(section) } else { visibleSections.remove(section) } } } struct BrowsingSettings_Previews: PreviewProvider { static var previews: some View { VStack { BrowsingSettings() } .injectFixtureEnvironmentObjects() } }