diff --git a/Shared/Coordinators/CustomizeSettingsCoordinator.swift b/Shared/Coordinators/CustomizeSettingsCoordinator.swift index 91cc4869a..824f4288f 100644 --- a/Shared/Coordinators/CustomizeSettingsCoordinator.swift +++ b/Shared/Coordinators/CustomizeSettingsCoordinator.swift @@ -19,6 +19,8 @@ final class CustomizeSettingsCoordinator: NavigationCoordinatable { @Route(.modal) var indicatorSettings = makeIndicatorSettings @Route(.modal) + var itemViewAttributes = makeItemViewAttributes + @Route(.push) var listColumnSettings = makeListColumnSettings func makeIndicatorSettings() -> NavigationViewCoordinator { @@ -27,6 +29,15 @@ final class CustomizeSettingsCoordinator: NavigationCoordinatable { } } + func makeItemViewAttributes(selection: Binding<[ItemViewAttribute]>) -> NavigationViewCoordinator { + NavigationViewCoordinator { + OrderedSectionSelectorView(selection: selection, sources: ItemViewAttribute.allCases) + .systemImage("list.bullet.rectangle.fill") + .navigationTitle(L10n.mediaAttributes.localizedCapitalized) + } + } + + @ViewBuilder func makeListColumnSettings(selection: Binding) -> some View { ListColumnsPickerView(selection: selection) } diff --git a/Shared/Coordinators/SettingsCoordinator.swift b/Shared/Coordinators/SettingsCoordinator.swift index 56b5cd170..57167b74f 100644 --- a/Shared/Coordinators/SettingsCoordinator.swift +++ b/Shared/Coordinators/SettingsCoordinator.swift @@ -44,6 +44,8 @@ final class SettingsCoordinator: NavigationCoordinatable { @Route(.push) var indicatorSettings = makeIndicatorSettings @Route(.push) + var itemViewAttributes = makeItemViewAttributes + @Route(.push) var serverConnection = makeServerConnection @Route(.push) var videoPlayerSettings = makeVideoPlayerSettings @@ -149,6 +151,12 @@ final class SettingsCoordinator: NavigationCoordinatable { IndicatorSettingsView() } + @ViewBuilder + func makeItemViewAttributes(selection: Binding<[ItemViewAttribute]>) -> some View { + OrderedSectionSelectorView(selection: selection, sources: ItemViewAttribute.allCases) + .navigationTitle(L10n.mediaAttributes.localizedCapitalized) + } + @ViewBuilder func makeServerConnection(server: ServerState) -> some View { EditServerView(server: server) diff --git a/Shared/Objects/ItemViewAttributes.swift b/Shared/Objects/ItemViewAttributes.swift new file mode 100644 index 000000000..f4848e95e --- /dev/null +++ b/Shared/Objects/ItemViewAttributes.swift @@ -0,0 +1,37 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2025 Jellyfin & Jellyfin Contributors +// + +import Defaults +import Foundation + +enum ItemViewAttribute: String, CaseIterable, Codable, Displayable, Defaults.Serializable { + + case ratingCritics + case ratingCommunity + case ratingOfficial + case videoQuality + case audioChannels + case subtitles + + var displayTitle: String { + switch self { + case .ratingCritics: + return L10n.criticRating + case .ratingCommunity: + return L10n.communityRating + case .ratingOfficial: + return L10n.parentalRating + case .videoQuality: + return L10n.video + case .audioChannels: + return L10n.audio + case .subtitles: + return L10n.subtitles + } + } +} diff --git a/Shared/Strings/Strings.swift b/Shared/Strings/Strings.swift index 4b9cd08b4..aac334e96 100644 --- a/Shared/Strings/Strings.swift +++ b/Shared/Strings/Strings.swift @@ -280,8 +280,8 @@ internal enum L10n { internal static let columns = L10n.tr("Localizable", "columns", fallback: "Columns") /// Community internal static let community = L10n.tr("Localizable", "community", fallback: "Community") - /// Community ratings - internal static let communityRatings = L10n.tr("Localizable", "communityRatings", fallback: "Community ratings") + /// Community rating + internal static let communityRating = L10n.tr("Localizable", "communityRating", fallback: "Community rating") /// Compact internal static let compact = L10n.tr("Localizable", "compact", fallback: "Compact") /// Compact Logo @@ -340,14 +340,14 @@ internal enum L10n { } /// Creator internal static let creator = L10n.tr("Localizable", "creator", fallback: "Creator") - /// Critic ratings - internal static let criticRatings = L10n.tr("Localizable", "criticRatings", fallback: "Critic ratings") + /// Critic rating + internal static let criticRating = L10n.tr("Localizable", "criticRating", fallback: "Critic rating") /// Critics internal static let critics = L10n.tr("Localizable", "critics", fallback: "Critics") /// Current internal static let current = L10n.tr("Localizable", "current", fallback: "Current") - /// Current Password - internal static let currentPassword = L10n.tr("Localizable", "currentPassword", fallback: "Current Password") + /// Current password + internal static let currentPassword = L10n.tr("Localizable", "currentPassword", fallback: "Current password") /// Custom internal static let custom = L10n.tr("Localizable", "custom", fallback: "Custom") /// Custom bitrate @@ -372,10 +372,10 @@ internal enum L10n { internal static let customFailedLogins = L10n.tr("Localizable", "customFailedLogins", fallback: "Custom failed logins") /// Customize internal static let customize = L10n.tr("Localizable", "customize", fallback: "Customize") - /// Custom Profile - internal static let customProfile = L10n.tr("Localizable", "customProfile", fallback: "Custom Profile") - /// Custom Rating - internal static let customRating = L10n.tr("Localizable", "customRating", fallback: "Custom Rating") + /// Custom profile + internal static let customProfile = L10n.tr("Localizable", "customProfile", fallback: "Custom profile") + /// Custom rating + internal static let customRating = L10n.tr("Localizable", "customRating", fallback: "Custom rating") /// Custom sessions internal static let customSessions = L10n.tr("Localizable", "customSessions", fallback: "Custom sessions") /// Daily @@ -388,10 +388,10 @@ internal enum L10n { internal static let dashboardDescription = L10n.tr("Localizable", "dashboardDescription", fallback: "Perform administrative tasks for your Jellyfin server.") /// Date Added internal static let dateAdded = L10n.tr("Localizable", "dateAdded", fallback: "Date Added") - /// Date Created - internal static let dateCreated = L10n.tr("Localizable", "dateCreated", fallback: "Date Created") - /// Date Modified - internal static let dateModified = L10n.tr("Localizable", "dateModified", fallback: "Date Modified") + /// Date created + internal static let dateCreated = L10n.tr("Localizable", "dateCreated", fallback: "Date created") + /// Date modified + internal static let dateModified = L10n.tr("Localizable", "dateModified", fallback: "Date modified") /// Date of death internal static let dateOfDeath = L10n.tr("Localizable", "dateOfDeath", fallback: "Date of death") /// Dates @@ -792,6 +792,8 @@ internal enum L10n { internal static let media = L10n.tr("Localizable", "media", fallback: "Media") /// Media Access internal static let mediaAccess = L10n.tr("Localizable", "mediaAccess", fallback: "Media Access") + /// Media attributes + internal static let mediaAttributes = L10n.tr("Localizable", "mediaAttributes", fallback: "Media attributes") /// Media downloads internal static let mediaDownloads = L10n.tr("Localizable", "mediaDownloads", fallback: "Media downloads") /// Media playback @@ -876,8 +878,8 @@ internal enum L10n { } /// No title internal static let noTitle = L10n.tr("Localizable", "noTitle", fallback: "No title") - /// Official Rating - internal static let officialRating = L10n.tr("Localizable", "officialRating", fallback: "Official Rating") + /// Official rating + internal static let officialRating = L10n.tr("Localizable", "officialRating", fallback: "Official rating") /// Offset internal static let offset = L10n.tr("Localizable", "offset", fallback: "Offset") /// OK @@ -906,8 +908,8 @@ internal enum L10n { internal static let overview = L10n.tr("Localizable", "overview", fallback: "Overview") /// Parental controls internal static let parentalControls = L10n.tr("Localizable", "parentalControls", fallback: "Parental controls") - /// Parental Rating - internal static let parentalRating = L10n.tr("Localizable", "parentalRating", fallback: "Parental Rating") + /// Parental rating + internal static let parentalRating = L10n.tr("Localizable", "parentalRating", fallback: "Parental rating") /// Password internal static let password = L10n.tr("Localizable", "password", fallback: "Password") /// User password has been changed. @@ -998,8 +1000,8 @@ internal enum L10n { internal static let quickConnectSuccessMessage = L10n.tr("Localizable", "quickConnectSuccessMessage", fallback: "Authorizing Quick Connect successful. Please continue on your other device.") /// Random internal static let random = L10n.tr("Localizable", "random", fallback: "Random") - /// Random Image - internal static let randomImage = L10n.tr("Localizable", "randomImage", fallback: "Random Image") + /// Random image + internal static let randomImage = L10n.tr("Localizable", "randomImage", fallback: "Random image") /// Rating internal static let rating = L10n.tr("Localizable", "rating", fallback: "Rating") /// %@ rating on a scale from 1 to 10. diff --git a/Shared/SwiftfinStore/StoredValue/StoredValues+User.swift b/Shared/SwiftfinStore/StoredValue/StoredValues+User.swift index f55f725d2..7a0d2e702 100644 --- a/Shared/SwiftfinStore/StoredValue/StoredValues+User.swift +++ b/Shared/SwiftfinStore/StoredValue/StoredValues+User.swift @@ -173,19 +173,11 @@ extension StoredValues.Keys { ) } - static var enableCriticRatings: Key { + static var itemViewAttributes: Key<[ItemViewAttribute]> { CurrentUserKey( - "enableCriticRatings", - domain: "enableCriticRatings", - default: true - ) - } - - static var enableCommunityRatings: Key { - CurrentUserKey( - "enableCommunityRatings", - domain: "enableCommunityRatings", - default: true + "itemViewAttributes", + domain: "itemViewAttributes", + default: ItemViewAttribute.allCases ) } } diff --git a/Swiftfin tvOS/Views/ItemView/Components/AttributeHStack.swift b/Swiftfin tvOS/Views/ItemView/Components/AttributeHStack.swift index 21ad17cb5..ebd8140e0 100644 --- a/Swiftfin tvOS/Views/ItemView/Components/AttributeHStack.swift +++ b/Swiftfin tvOS/Views/ItemView/Components/AttributeHStack.swift @@ -6,26 +6,30 @@ // Copyright (c) 2025 Jellyfin & Jellyfin Contributors // -import Defaults import SwiftUI extension ItemView { - struct AttributesHStack: View { - @ObservedObject var viewModel: ItemViewModel - @StoredValue(.User.enableCriticRatings) - private var enableCriticRatings - - @StoredValue(.User.enableCommunityRatings) - private var enableCommunityRatings + @StoredValue(.User.itemViewAttributes) + private var itemViewAttributes var body: some View { HStack(spacing: 25) { + ForEach(itemViewAttributes, id: \.self) { attribute in + getAttribute(attribute) + } + } + .foregroundColor(Color(UIColor.darkGray)) + } - if let criticRating = viewModel.item.criticRating, enableCriticRatings { + @ViewBuilder + func getAttribute(_ attribute: ItemViewAttribute) -> some View { + switch attribute { + case .ratingCritics: + if let criticRating = viewModel.item.criticRating { HStack(spacing: 2) { Group { if criticRating >= 60 { @@ -41,8 +45,8 @@ extension ItemView { } .asAttributeStyle(.outline) } - - if let communityRating = viewModel.item.communityRating, enableCommunityRatings { + case .ratingCommunity: + if let communityRating = viewModel.item.communityRating { HStack(spacing: 2) { Image(systemName: "star.fill") .font(.caption2) @@ -51,41 +55,35 @@ extension ItemView { } .asAttributeStyle(.outline) } - + case .ratingOfficial: if let officialRating = viewModel.item.officialRating { Text(officialRating) .asAttributeStyle(.outline) } - - if let mediaStreams = viewModel.selectedMediaSource?.mediaStreams { - - if mediaStreams.hasHDVideo { - Text("HD") - .asAttributeStyle(.fill) - } - - if mediaStreams.has4KVideo { - Text("4K") - .asAttributeStyle(.fill) - } - - if mediaStreams.has51AudioChannelLayout { - Text("5.1") - .asAttributeStyle(.fill) - } - - if mediaStreams.has71AudioChannelLayout { - Text("7.1") - .asAttributeStyle(.fill) - } - - if mediaStreams.hasSubtitles { - Text("CC") - .asAttributeStyle(.outline) - } + case .videoQuality: + if viewModel.selectedMediaSource?.mediaStreams?.hasHDVideo == true { + Text("HD") + .asAttributeStyle(.fill) + } + if viewModel.selectedMediaSource?.mediaStreams?.has4KVideo == true { + Text("4K") + .asAttributeStyle(.fill) + } + case .audioChannels: + if viewModel.selectedMediaSource?.mediaStreams?.has51AudioChannelLayout == true { + Text("5.1") + .asAttributeStyle(.fill) + } + if viewModel.selectedMediaSource?.mediaStreams?.has71AudioChannelLayout == true { + Text("7.1") + .asAttributeStyle(.fill) + } + case .subtitles: + if viewModel.selectedMediaSource?.mediaStreams?.hasSubtitles == true { + Text("CC") + .asAttributeStyle(.outline) } } - .foregroundColor(Color(UIColor.darkGray)) } } } diff --git a/Swiftfin tvOS/Views/SettingsView/CustomizeViewsSettings/Components/Sections/ItemSection.swift b/Swiftfin tvOS/Views/SettingsView/CustomizeViewsSettings/Components/Sections/ItemSection.swift index ed2b1e4c9..861d8f008 100644 --- a/Swiftfin tvOS/Views/SettingsView/CustomizeViewsSettings/Components/Sections/ItemSection.swift +++ b/Swiftfin tvOS/Views/SettingsView/CustomizeViewsSettings/Components/Sections/ItemSection.swift @@ -17,10 +17,11 @@ extension CustomizeViewsSettings { @Injected(\.currentUserSession) private var userSession - @StoredValue(.User.enableCriticRatings) - private var enableCriticRatings - @StoredValue(.User.enableCommunityRatings) - private var enableCommunityRatings + @EnvironmentObject + private var router: CustomizeSettingsCoordinator.Router + + @StoredValue(.User.itemViewAttributes) + private var itemViewAttributes @StoredValue(.User.enableItemEditing) private var enableItemEditing @@ -32,29 +33,22 @@ extension CustomizeViewsSettings { var body: some View { Section(L10n.items) { - Toggle(L10n.criticRatings, isOn: $enableCriticRatings) - - Toggle(L10n.communityRatings, isOn: $enableCommunityRatings) - } - - if userSession?.user.permissions.items.canEditMetadata ?? false || - userSession?.user.permissions.items.canDelete ?? false || - userSession?.user.permissions.items.canManageCollections ?? false - { - Section(L10n.management) { - - /// Enable Refreshing Items from All Visible LIbraries - if userSession?.user.permissions.items.canEditMetadata ?? false { - Toggle(L10n.allowItemEditing, isOn: $enableItemEditing) - } - /// Enable Deleting Items from Approved Libraries - if userSession?.user.permissions.items.canDelete ?? false { - Toggle(L10n.allowItemDeletion, isOn: $enableItemDeletion) - } - /// Enable Refreshing & Deleting Collections - if userSession?.user.permissions.items.canManageCollections ?? false { - Toggle(L10n.allowCollectionManagement, isOn: $enableCollectionManagement) + ChevronButton(L10n.mediaAttributes) + .onSelect { + router.route(to: \.itemViewAttributes, $itemViewAttributes) } + + /// Enable Refreshing Items from All Visible LIbraries + if userSession?.user.permissions.items.canEditMetadata ?? false { + Toggle(L10n.allowItemEditing, isOn: $enableItemEditing) + } + /// Enable Deleting Items from Approved Libraries + if userSession?.user.permissions.items.canDelete ?? false { + Toggle(L10n.allowItemDeletion, isOn: $enableItemDeletion) + } + /// Enable Refreshing & Deleting Collections + if userSession?.user.permissions.items.canManageCollections ?? false { + Toggle(L10n.allowCollectionManagement, isOn: $enableCollectionManagement) } } } diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index 5bbabb062..673078bc6 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -33,6 +33,8 @@ 4E17498F2CC00A3100DD07D1 /* DeviceInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E17498D2CC00A2E00DD07D1 /* DeviceInfo.swift */; }; 4E182C9C2C94993200FBEFD5 /* ServerTasksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E182C9B2C94993200FBEFD5 /* ServerTasksView.swift */; }; 4E182C9F2C94A1E000FBEFD5 /* ServerTaskRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E182C9E2C94A1E000FBEFD5 /* ServerTaskRow.swift */; }; + 4E1A39332D56C84200BAC1C7 /* ItemViewAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E1A39322D56C83E00BAC1C7 /* ItemViewAttributes.swift */; }; + 4E1A39342D56C84200BAC1C7 /* ItemViewAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E1A39322D56C83E00BAC1C7 /* ItemViewAttributes.swift */; }; 4E1AA0042D0640AA00524970 /* RemoteImageInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E1AA0032D0640A400524970 /* RemoteImageInfo.swift */; }; 4E1AA0052D0640AA00524970 /* RemoteImageInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E1AA0032D0640A400524970 /* RemoteImageInfo.swift */; }; 4E204E592C574FD9004D22A2 /* CustomizeSettingsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E204E582C574FD9004D22A2 /* CustomizeSettingsCoordinator.swift */; }; @@ -1227,6 +1229,7 @@ 4E17498D2CC00A2E00DD07D1 /* DeviceInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceInfo.swift; sourceTree = ""; }; 4E182C9B2C94993200FBEFD5 /* ServerTasksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerTasksView.swift; sourceTree = ""; }; 4E182C9E2C94A1E000FBEFD5 /* ServerTaskRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerTaskRow.swift; sourceTree = ""; }; + 4E1A39322D56C83E00BAC1C7 /* ItemViewAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemViewAttributes.swift; sourceTree = ""; }; 4E1AA0032D0640A400524970 /* RemoteImageInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteImageInfo.swift; sourceTree = ""; }; 4E204E582C574FD9004D22A2 /* CustomizeSettingsCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeSettingsCoordinator.swift; sourceTree = ""; }; 4E2182E42CAF67EF0094806B /* PlayMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayMethod.swift; sourceTree = ""; }; @@ -3199,6 +3202,7 @@ 4EFE0C7F2D02054300D4834D /* ItemArrayElements.swift */, E14EDECA2B8FB66F000F00A4 /* ItemFilter */, E1C925F328875037002A7A66 /* ItemViewType.swift */, + 4E1A39322D56C83E00BAC1C7 /* ItemViewAttributes.swift */, E13F05EB28BC9000003499D2 /* LibraryDisplayType.swift */, E1DE2B4E2B983F3200F6715F /* LibraryParent */, 4E2AC4C02C6C48EB00DD600D /* MediaComponents */, @@ -5723,6 +5727,7 @@ 53ABFDE5267974EF00886593 /* ViewModel.swift in Sources */, E148128628C15475003B8787 /* SortOrder+ItemSortOrder.swift in Sources */, E1CB75722C80E71800217C76 /* DirectPlayProfile.swift in Sources */, + 4E1A39332D56C84200BAC1C7 /* ItemViewAttributes.swift in Sources */, E1E1E24E28DF8A2E000DF5FD /* PreferenceKeys.swift in Sources */, E1575E9B293E7B1E001665B1 /* EnvironmentValue+Keys.swift in Sources */, E133328929538D8D00EE76AB /* Files.swift in Sources */, @@ -6447,6 +6452,7 @@ E1D3043528D1763100587289 /* SeeAllButton.swift in Sources */, 4E73E2A62C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift in Sources */, E172D3B22BACA569007B4647 /* EpisodeContent.swift in Sources */, + 4E1A39342D56C84200BAC1C7 /* ItemViewAttributes.swift in Sources */, 4EC1C8522C7FDFA300E2879E /* PlaybackDeviceProfile.swift in Sources */, 4EA09DE12CC4E4F100CB27E4 /* APIKeysView.swift in Sources */, DFB7C3DF2C7AA43A00CE7CDC /* UserSignInState.swift in Sources */, diff --git a/Swiftfin/Views/ItemView/Components/AttributeHStack.swift b/Swiftfin/Views/ItemView/Components/AttributeHStack.swift index 1911e0b6f..5d1480984 100644 --- a/Swiftfin/Views/ItemView/Components/AttributeHStack.swift +++ b/Swiftfin/Views/ItemView/Components/AttributeHStack.swift @@ -6,25 +6,30 @@ // Copyright (c) 2025 Jellyfin & Jellyfin Contributors // -import Defaults import SwiftUI extension ItemView { - struct AttributesHStack: View { - @ObservedObject var viewModel: ItemViewModel - @StoredValue(.User.enableCriticRatings) - private var enableCriticRatings - - @StoredValue(.User.enableCommunityRatings) - private var enableCommunityRatings + @StoredValue(.User.itemViewAttributes) + private var itemViewAttributes var body: some View { HStack { - if let criticRating = viewModel.item.criticRating, enableCriticRatings { + ForEach(itemViewAttributes, id: \.self) { attribute in + getAttribute(attribute) + } + } + .foregroundColor(Color(UIColor.darkGray)) + } + + @ViewBuilder + func getAttribute(_ attribute: ItemViewAttribute) -> some View { + switch attribute { + case .ratingCritics: + if let criticRating = viewModel.item.criticRating { HStack(spacing: 2) { Group { if criticRating >= 60 { @@ -40,8 +45,8 @@ extension ItemView { } .asAttributeStyle(.outline) } - - if let communityRating = viewModel.item.communityRating, enableCommunityRatings { + case .ratingCommunity: + if let communityRating = viewModel.item.communityRating { HStack(spacing: 2) { Image(systemName: "star.fill") .font(.caption2) @@ -50,41 +55,35 @@ extension ItemView { } .asAttributeStyle(.outline) } - + case .ratingOfficial: if let officialRating = viewModel.item.officialRating { Text(officialRating) .asAttributeStyle(.outline) } - - if let mediaStreams = viewModel.selectedMediaSource?.mediaStreams { - - if mediaStreams.hasHDVideo { - Text("HD") - .asAttributeStyle(.fill) - } - - if mediaStreams.has4KVideo { - Text("4K") - .asAttributeStyle(.fill) - } - - if mediaStreams.has51AudioChannelLayout { - Text("5.1") - .asAttributeStyle(.fill) - } - - if mediaStreams.has71AudioChannelLayout { - Text("7.1") - .asAttributeStyle(.fill) - } - - if mediaStreams.hasSubtitles { - Text("CC") - .asAttributeStyle(.outline) - } + case .videoQuality: + if viewModel.selectedMediaSource?.mediaStreams?.hasHDVideo == true { + Text("HD") + .asAttributeStyle(.fill) + } + if viewModel.selectedMediaSource?.mediaStreams?.has4KVideo == true { + Text("4K") + .asAttributeStyle(.fill) + } + case .audioChannels: + if viewModel.selectedMediaSource?.mediaStreams?.has51AudioChannelLayout == true { + Text("5.1") + .asAttributeStyle(.fill) + } + if viewModel.selectedMediaSource?.mediaStreams?.has71AudioChannelLayout == true { + Text("7.1") + .asAttributeStyle(.fill) + } + case .subtitles: + if viewModel.selectedMediaSource?.mediaStreams?.hasSubtitles == true { + Text("CC") + .asAttributeStyle(.outline) } } - .foregroundColor(Color(UIColor.darkGray)) } } } diff --git a/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/ItemSection.swift b/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/ItemSection.swift index 8ea3b2ca3..713871c34 100644 --- a/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/ItemSection.swift +++ b/Swiftfin/Views/SettingsView/CustomizeViewsSettings/Components/Sections/ItemSection.swift @@ -17,10 +17,11 @@ extension CustomizeViewsSettings { @Injected(\.currentUserSession) private var userSession - @StoredValue(.User.enableCriticRatings) - private var enableCriticRatings - @StoredValue(.User.enableCommunityRatings) - private var enableCommunityRatings + @EnvironmentObject + private var router: SettingsCoordinator.Router + + @StoredValue(.User.itemViewAttributes) + private var itemViewAttributes @StoredValue(.User.enableItemEditing) private var enableItemEditing @@ -32,44 +33,35 @@ extension CustomizeViewsSettings { var body: some View { Section(L10n.items) { - Toggle(L10n.criticRatings, isOn: $enableCriticRatings) - - Toggle(L10n.communityRatings, isOn: $enableCommunityRatings) - } - - if userSession?.user.permissions.items.canEditMetadata ?? false - || userSession?.user.permissions.items.canDelete ?? false - // || userSession?.user.permissions.items.canDownload ?? false - || userSession?.user.permissions.items.canManageCollections ?? false - // || userSession?.user.permissions.items.canManageLyrics ?? false - // || userSession?.user.permissions.items.canManageSubtitles - { - Section(L10n.management) { - /// Enable Editing Items from All Visible LIbraries - if userSession?.user.permissions.items.canEditMetadata ?? false { - Toggle(L10n.allowItemEditing, isOn: $enableItemEditing) - } - /// Enable Deleting Items from Approved Libraries - if userSession?.user.permissions.items.canDelete ?? false { - Toggle(L10n.allowItemDeletion, isOn: $enableItemDeletion) + ChevronButton(L10n.mediaAttributes) + .onSelect { + router.route(to: \.itemViewAttributes, $itemViewAttributes) } - /// Enable Downloading All Items - /* if userSession?.user.permissions.items.canDownload ?? false { - Toggle(L10n.allowItemDownloading, isOn: $enableItemDownloads) - } */ - /// Enable Deleting or Editing Collections - if userSession?.user.permissions.items.canManageCollections ?? false { - Toggle(L10n.allowCollectionManagement, isOn: $enableCollectionManagement) - } - /// Manage Item Lyrics - /* if userSession?.user.permissions.items.canManageLyrics ?? false { - Toggle(L10n.allowLyricsManagement isOn: $enableLyricsManagement) - } */ - /// Manage Item Subtitles - /* if userSession?.user.items.canManageSubtitles ?? false { - Toggle(L10n.allowSubtitleManagement, isOn: $enableSubtitleManagement) - } */ + + /// Enable Editing Items from All Visible LIbraries + if userSession?.user.permissions.items.canEditMetadata ?? false { + Toggle(L10n.allowItemEditing, isOn: $enableItemEditing) + } + /// Enable Deleting Items from Approved Libraries + if userSession?.user.permissions.items.canDelete ?? false { + Toggle(L10n.allowItemDeletion, isOn: $enableItemDeletion) + } + /// Enable Downloading All Items + /* if userSession?.user.permissions.items.canDownload ?? false { + Toggle(L10n.allowItemDownloading, isOn: $enableItemDownloads) + } */ + /// Enable Deleting or Editing Collections + if userSession?.user.permissions.items.canManageCollections ?? false { + Toggle(L10n.allowCollectionManagement, isOn: $enableCollectionManagement) } + /// Manage Item Lyrics + /* if userSession?.user.permissions.items.canManageLyrics ?? false { + Toggle(L10n.allowLyricsManagement isOn: $enableLyricsManagement) + } */ + /// Manage Item Subtitles + /* if userSession?.user.items.canManageSubtitles ?? false { + Toggle(L10n.allowSubtitleManagement, isOn: $enableSubtitleManagement) + } */ } } } diff --git a/Translations/en.lproj/Localizable.strings b/Translations/en.lproj/Localizable.strings index 99e0a56c2..f8fb06726 100644 --- a/Translations/en.lproj/Localizable.strings +++ b/Translations/en.lproj/Localizable.strings @@ -394,8 +394,8 @@ /// Community "community" = "Community"; -/// Community ratings -"communityRatings" = "Community ratings"; +/// Community rating +"communityRating" = "Community rating"; /// Compact "compact" = "Compact"; @@ -481,8 +481,8 @@ /// Creator "creator" = "Creator"; -/// Critic ratings -"criticRatings" = "Critic ratings"; +/// Critic rating +"criticRating" = "Critic rating"; /// Critics "critics" = "Critics"; @@ -490,8 +490,8 @@ /// Current "current" = "Current"; -/// Current Password -"currentPassword" = "Current Password"; +/// Current password +"currentPassword" = "Current password"; /// Custom "custom" = "Custom"; @@ -526,11 +526,11 @@ /// Customize "customize" = "Customize"; -/// Custom Profile -"customProfile" = "Custom Profile"; +/// Custom profile +"customProfile" = "Custom profile"; -/// Custom Rating -"customRating" = "Custom Rating"; +/// Custom rating +"customRating" = "Custom rating"; /// Custom sessions "customSessions" = "Custom sessions"; @@ -550,11 +550,11 @@ /// Date Added "dateAdded" = "Date Added"; -/// Date Created -"dateCreated" = "Date Created"; +/// Date created +"dateCreated" = "Date created"; -/// Date Modified -"dateModified" = "Date Modified"; +/// Date modified +"dateModified" = "Date modified"; /// Date of death "dateOfDeath" = "Date of death"; @@ -1123,6 +1123,9 @@ /// Media Access "mediaAccess" = "Media Access"; +/// Media attributes +"mediaAttributes" = "Media attributes"; + /// Media downloads "mediaDownloads" = "Media downloads"; @@ -1246,8 +1249,8 @@ /// No title "noTitle" = "No title"; -/// Official Rating -"officialRating" = "Official Rating"; +/// Official rating +"officialRating" = "Official rating"; /// Offset "offset" = "Offset"; @@ -1291,8 +1294,8 @@ /// Parental controls "parentalControls" = "Parental controls"; -/// Parental Rating -"parentalRating" = "Parental Rating"; +/// Parental rating +"parentalRating" = "Parental rating"; /// Password "password" = "Password"; @@ -1429,8 +1432,8 @@ /// Random "random" = "Random"; -/// Random Image -"randomImage" = "Random Image"; +/// Random image +"randomImage" = "Random image"; /// Rating "rating" = "Rating";