[tvOS] Mirror iOS Ratings + Attribute Settings (#1422)
* Copy + Paste + Settings * Much bigger changes to allow attribute customization. * wip --------- Co-authored-by: Ethan Pippin <ethanpippin2343@gmail.com>
This commit is contained in:
parent
846aabc868
commit
c113c341bf
|
@ -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<BasicNavigationViewCoordinator> {
|
||||
|
@ -27,6 +29,15 @@ final class CustomizeSettingsCoordinator: NavigationCoordinatable {
|
|||
}
|
||||
}
|
||||
|
||||
func makeItemViewAttributes(selection: Binding<[ItemViewAttribute]>) -> NavigationViewCoordinator<BasicNavigationViewCoordinator> {
|
||||
NavigationViewCoordinator {
|
||||
OrderedSectionSelectorView(selection: selection, sources: ItemViewAttribute.allCases)
|
||||
.systemImage("list.bullet.rectangle.fill")
|
||||
.navigationTitle(L10n.mediaAttributes.localizedCapitalized)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func makeListColumnSettings(selection: Binding<Int>) -> some View {
|
||||
ListColumnsPickerView(selection: selection)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
enum ItemViewAttribute: String, CaseIterable, Displayable, Storable {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -280,6 +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 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
|
||||
|
@ -338,12 +340,14 @@ internal enum L10n {
|
|||
}
|
||||
/// Creator
|
||||
internal static let creator = L10n.tr("Localizable", "creator", fallback: "Creator")
|
||||
/// 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
|
||||
|
@ -368,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
|
||||
|
@ -384,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
|
||||
|
@ -788,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
|
||||
|
@ -872,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
|
||||
|
@ -902,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.
|
||||
|
@ -994,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.
|
||||
|
|
|
@ -172,5 +172,13 @@ extension StoredValues.Keys {
|
|||
default: false
|
||||
)
|
||||
}
|
||||
|
||||
static var itemViewAttributes: Key<[ItemViewAttribute]> {
|
||||
CurrentUserKey(
|
||||
"itemViewAttributes",
|
||||
domain: "itemViewAttributes",
|
||||
default: ItemViewAttribute.allCases
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,49 +9,81 @@
|
|||
import SwiftUI
|
||||
|
||||
extension ItemView {
|
||||
|
||||
struct AttributesHStack: View {
|
||||
|
||||
@ObservedObject
|
||||
var viewModel: ItemViewModel
|
||||
|
||||
@StoredValue(.User.itemViewAttributes)
|
||||
private var itemViewAttributes
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 25) {
|
||||
ForEach(itemViewAttributes, id: \.self) { attribute in
|
||||
getAttribute(attribute)
|
||||
}
|
||||
}
|
||||
.foregroundStyle(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 {
|
||||
Image(.tomatoFresh)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
} else {
|
||||
Image(.tomatoRotten)
|
||||
}
|
||||
}
|
||||
.font(.caption2)
|
||||
|
||||
Text("\(criticRating, specifier: "%.0f")")
|
||||
}
|
||||
.asAttributeStyle(.outline)
|
||||
}
|
||||
case .ratingCommunity:
|
||||
if let communityRating = viewModel.item.communityRating {
|
||||
HStack(spacing: 2) {
|
||||
Image(systemName: "star.fill")
|
||||
.font(.caption2)
|
||||
|
||||
Text("\(communityRating, specifier: "%.1f")")
|
||||
}
|
||||
.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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,12 @@ extension CustomizeViewsSettings {
|
|||
@Injected(\.currentUserSession)
|
||||
private var userSession
|
||||
|
||||
@EnvironmentObject
|
||||
private var router: CustomizeSettingsCoordinator.Router
|
||||
|
||||
@StoredValue(.User.itemViewAttributes)
|
||||
private var itemViewAttributes
|
||||
|
||||
@StoredValue(.User.enableItemEditing)
|
||||
private var enableItemEditing
|
||||
@StoredValue(.User.enableItemDeletion)
|
||||
|
@ -25,24 +31,24 @@ extension CustomizeViewsSettings {
|
|||
private var enableCollectionManagement
|
||||
|
||||
var body: some View {
|
||||
if userSession?.user.permissions.items.canEditMetadata ?? false ||
|
||||
userSession?.user.permissions.items.canDelete ?? false ||
|
||||
userSession?.user.permissions.items.canManageCollections ?? false
|
||||
{
|
||||
Section(L10n.items) {
|
||||
|
||||
Section(L10n.items) {
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */; };
|
||||
|
@ -1285,6 +1287,7 @@
|
|||
4E17498D2CC00A2E00DD07D1 /* DeviceInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceInfo.swift; sourceTree = "<group>"; };
|
||||
4E182C9B2C94993200FBEFD5 /* ServerTasksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerTasksView.swift; sourceTree = "<group>"; };
|
||||
4E182C9E2C94A1E000FBEFD5 /* ServerTaskRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerTaskRow.swift; sourceTree = "<group>"; };
|
||||
4E1A39322D56C83E00BAC1C7 /* ItemViewAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemViewAttributes.swift; sourceTree = "<group>"; };
|
||||
4E1AA0032D0640A400524970 /* RemoteImageInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteImageInfo.swift; sourceTree = "<group>"; };
|
||||
4E204E582C574FD9004D22A2 /* CustomizeSettingsCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeSettingsCoordinator.swift; sourceTree = "<group>"; };
|
||||
4E2182E42CAF67EF0094806B /* PlayMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayMethod.swift; sourceTree = "<group>"; };
|
||||
|
@ -3315,6 +3318,7 @@
|
|||
4EFE0C7F2D02054300D4834D /* ItemArrayElements.swift */,
|
||||
E14EDECA2B8FB66F000F00A4 /* ItemFilter */,
|
||||
E1C925F328875037002A7A66 /* ItemViewType.swift */,
|
||||
4E1A39322D56C83E00BAC1C7 /* ItemViewAttributes.swift */,
|
||||
E13F05EB28BC9000003499D2 /* LibraryDisplayType.swift */,
|
||||
E1DE2B4E2B983F3200F6715F /* LibraryParent */,
|
||||
4E2AC4C02C6C48EB00DD600D /* MediaComponents */,
|
||||
|
@ -6158,6 +6162,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 */,
|
||||
|
@ -6882,6 +6887,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 */,
|
||||
|
|
|
@ -9,14 +9,26 @@
|
|||
import SwiftUI
|
||||
|
||||
extension ItemView {
|
||||
|
||||
struct AttributesHStack: View {
|
||||
|
||||
@ObservedObject
|
||||
var viewModel: ItemViewModel
|
||||
|
||||
@StoredValue(.User.itemViewAttributes)
|
||||
private var itemViewAttributes
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
ForEach(itemViewAttributes, id: \.self) { attribute in
|
||||
getAttribute(attribute)
|
||||
}
|
||||
}
|
||||
.foregroundStyle(Color(UIColor.darkGray))
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func getAttribute(_ attribute: ItemViewAttribute) -> some View {
|
||||
switch attribute {
|
||||
case .ratingCritics:
|
||||
if let criticRating = viewModel.item.criticRating {
|
||||
HStack(spacing: 2) {
|
||||
Group {
|
||||
|
@ -33,7 +45,7 @@ extension ItemView {
|
|||
}
|
||||
.asAttributeStyle(.outline)
|
||||
}
|
||||
|
||||
case .ratingCommunity:
|
||||
if let communityRating = viewModel.item.communityRating {
|
||||
HStack(spacing: 2) {
|
||||
Image(systemName: "star.fill")
|
||||
|
@ -43,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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,12 @@ extension CustomizeViewsSettings {
|
|||
@Injected(\.currentUserSession)
|
||||
private var userSession
|
||||
|
||||
@EnvironmentObject
|
||||
private var router: SettingsCoordinator.Router
|
||||
|
||||
@StoredValue(.User.itemViewAttributes)
|
||||
private var itemViewAttributes
|
||||
|
||||
@StoredValue(.User.enableItemEditing)
|
||||
private var enableItemEditing
|
||||
@StoredValue(.User.enableItemDeletion)
|
||||
|
@ -25,39 +31,37 @@ extension CustomizeViewsSettings {
|
|||
private var enableCollectionManagement
|
||||
|
||||
var body: some View {
|
||||
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.items) {
|
||||
/// Enable Editing Items from All Visible LIbraries
|
||||
if userSession?.user.permissions.items.canEditMetadata ?? false {
|
||||
Toggle(L10n.allowItemEditing, isOn: $enableItemEditing)
|
||||
Section(L10n.items) {
|
||||
|
||||
ChevronButton(L10n.mediaAttributes)
|
||||
.onSelect {
|
||||
router.route(to: \.itemViewAttributes, $itemViewAttributes)
|
||||
}
|
||||
/// 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)
|
||||
} */
|
||||
|
||||
/// 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)
|
||||
} */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -394,6 +394,9 @@
|
|||
/// Community
|
||||
"community" = "Community";
|
||||
|
||||
/// Community rating
|
||||
"communityRating" = "Community rating";
|
||||
|
||||
/// Compact
|
||||
"compact" = "Compact";
|
||||
|
||||
|
@ -478,14 +481,17 @@
|
|||
/// Creator
|
||||
"creator" = "Creator";
|
||||
|
||||
/// Critic rating
|
||||
"criticRating" = "Critic rating";
|
||||
|
||||
/// Critics
|
||||
"critics" = "Critics";
|
||||
|
||||
/// Current
|
||||
"current" = "Current";
|
||||
|
||||
/// Current Password
|
||||
"currentPassword" = "Current Password";
|
||||
/// Current password
|
||||
"currentPassword" = "Current password";
|
||||
|
||||
/// Custom
|
||||
"custom" = "Custom";
|
||||
|
@ -520,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";
|
||||
|
@ -544,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";
|
||||
|
@ -1117,6 +1123,9 @@
|
|||
/// Media Access
|
||||
"mediaAccess" = "Media Access";
|
||||
|
||||
/// Media attributes
|
||||
"mediaAttributes" = "Media attributes";
|
||||
|
||||
/// Media downloads
|
||||
"mediaDownloads" = "Media downloads";
|
||||
|
||||
|
@ -1240,8 +1249,8 @@
|
|||
/// No title
|
||||
"noTitle" = "No title";
|
||||
|
||||
/// Official Rating
|
||||
"officialRating" = "Official Rating";
|
||||
/// Official rating
|
||||
"officialRating" = "Official rating";
|
||||
|
||||
/// Offset
|
||||
"offset" = "Offset";
|
||||
|
@ -1285,8 +1294,8 @@
|
|||
/// Parental controls
|
||||
"parentalControls" = "Parental controls";
|
||||
|
||||
/// Parental Rating
|
||||
"parentalRating" = "Parental Rating";
|
||||
/// Parental rating
|
||||
"parentalRating" = "Parental rating";
|
||||
|
||||
/// Password
|
||||
"password" = "Password";
|
||||
|
@ -1423,8 +1432,8 @@
|
|||
/// Random
|
||||
"random" = "Random";
|
||||
|
||||
/// Random Image
|
||||
"randomImage" = "Random Image";
|
||||
/// Random image
|
||||
"randomImage" = "Random image";
|
||||
|
||||
/// Rating
|
||||
"rating" = "Rating";
|
||||
|
|
Loading…
Reference in New Issue