Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[iOS] Media Item Menu | Edit Metadata #1323

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
59b0896
Playback Quality - Learn More
JPKribs Nov 17, 2024
44b3b13
TODO: Fix leading not working on second line.
JPKribs Nov 17, 2024
76b65ce
Remove layoutDirection.
JPKribs Nov 17, 2024
32a88b2
Implement for tvOS. Slightly different spacing.
JPKribs Nov 18, 2024
098aba5
VStack
JPKribs Nov 18, 2024
11aab22
WIP - tvOS Implementaiton. SUBJECT TO CHANGE / ELIMINATION.
JPKribs Nov 18, 2024
5ccd6ca
Background Icon & formatting
JPKribs Nov 19, 2024
2891287
wip
LePips Nov 20, 2024
ef053f9
Review Changes. Remove unused Strings, clean up comments.
JPKribs Nov 21, 2024
935e6bc
Remove duplicate items used for testing
JPKribs Nov 21, 2024
7c4f379
Remove tvOS scrollIfLargerThanContainer for now.
JPKribs Nov 21, 2024
12c6737
Merge branch 'jellyfin:main' into itemMetadata
JPKribs Nov 21, 2024
0a02c8d
Edit Text-based Metadata
JPKribs Nov 21, 2024
6f6b2a0
Merge remote-tracking branch 'refs/remotes/origin/itemMetadata'
JPKribs Nov 21, 2024
3b1199b
ViewModel Cleanup
JPKribs Nov 24, 2024
9031ffc
use binding extensions
LePips Nov 27, 2024
011b523
Huge overhaul:
JPKribs Nov 27, 2024
95599c7
Merge remote-tracking branch 'refs/remotes/origin/itemMetadata'
JPKribs Nov 27, 2024
962f6a7
String fixes & overview size
JPKribs Nov 27, 2024
c25cdf0
Merge branch 'main' into itemMetadata
JPKribs Nov 27, 2024
500b865
Fix build issues & String cleanup
JPKribs Nov 27, 2024
6f3f1d2
fix overview sizing, cleanup
LePips Nov 28, 2024
4075bd0
itemMetadataWasEdited -> temMetadataDidChange
JPKribs Nov 28, 2024
b1f5939
Creation of the NavigationBarMenuButtonModifier for an "ellipsis.circ…
JPKribs Nov 30, 2024
cb6354e
Custom vs Official Rating + Menu Button Label change
JPKribs Nov 30, 2024
e5d458c
Menu button spacing and groundwork for other menu items (canDownload)…
JPKribs Nov 30, 2024
25cfc48
Linting
JPKribs Nov 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Shared/Coordinators/ItemEditorCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,19 @@ final class ItemEditorCoordinator: ObservableObject, NavigationCoordinatable {

private let item: BaseItemDto

@Route(.modal)
var editMetadata = makeEditMetadata

init(item: BaseItemDto) {
self.item = item
}

func makeEditMetadata(item: BaseItemDto) -> NavigationViewCoordinator<BasicNavigationViewCoordinator> {
NavigationViewCoordinator {
EditMetadataView(viewModel: ItemEditorViewModel(item: item))
}
}

@ViewBuilder
func makeStart() -> some View {
ItemEditorView(item: item)
Expand Down
62 changes: 62 additions & 0 deletions Shared/Extensions/Binding.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// 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) 2024 Jellyfin & Jellyfin Contributors
//

import SwiftUI

extension Binding {

func clamp(min: Value, max: Value) -> Binding<Value> where Value: Comparable {
Binding<Value>(
get: { Swift.min(Swift.max(wrappedValue, min), max) },
set: { wrappedValue = Swift.min(Swift.max($0, min), max) }
)
}

func coalesce<T>(_ defaultValue: T) -> Binding<T> where Value == T? {
Binding<T>(
get: { wrappedValue ?? defaultValue },
set: { wrappedValue = $0 }
)
}

func map<V>(getter: @escaping (Value) -> V, setter: @escaping (V) -> Value) -> Binding<V> {
Binding<V>(
get: { getter(wrappedValue) },
set: { wrappedValue = setter($0) }
)
}

func min(_ minValue: Value) -> Binding<Value> where Value: Comparable {
Binding<Value>(
get: { Swift.max(wrappedValue, minValue) },
set: { wrappedValue = Swift.max($0, minValue) }
)
}

func negate() -> Binding<Bool> where Value == Bool {
map(getter: { !$0 }, setter: { $0 })
}
}

extension Binding where Value: RangeReplaceableCollection, Value.Element: Equatable {

func contains(_ element: Value.Element) -> Binding<Bool> {
Binding<Bool>(
get: { wrappedValue.contains(element) },
set: { newValue in
if newValue {
if !wrappedValue.contains(element) {
wrappedValue.append(element)
}
} else {
wrappedValue.removeAll { $0 == element }
}
}
)
}
}
25 changes: 25 additions & 0 deletions Shared/Extensions/FormatStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import SwiftUI

// TODO: break into separate files

struct HourMinuteFormatStyle: FormatStyle {

func format(_ value: TimeInterval) -> String {
Expand Down Expand Up @@ -78,6 +80,29 @@ extension ParseableFormatStyle where Self == DayIntervalParseableFormatStyle {
}
}

struct NilIfEmptyStringFormatStyle: ParseableFormatStyle {

var parseStrategy: NilIfEmptyStringParseStrategy = .init()

func format(_ value: String?) -> String {
value ?? ""
}
}

struct NilIfEmptyStringParseStrategy: ParseStrategy {

func parse(_ value: String) -> String? {
value.isEmpty ? nil : value
}
}

extension ParseableFormatStyle where Self == NilIfEmptyStringFormatStyle {

static var nilIfEmptyString: NilIfEmptyStringFormatStyle {
.init()
}
}

extension FormatStyle where Self == TimeIntervalFormatStyle {

static func interval(
Expand Down
35 changes: 35 additions & 0 deletions Shared/Extensions/JellyfinAPI/MetadataField.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// 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) 2024 Jellyfin & Jellyfin Contributors
//

import Foundation
import JellyfinAPI

extension MetadataField: Displayable {
var displayTitle: String {
switch self {
case .cast:
return L10n.people
case .genres:
return L10n.genres
case .productionLocations:
return L10n.productionLocations
case .studios:
return L10n.studios
case .tags:
return L10n.tags
case .name:
return L10n.name
case .overview:
return L10n.overview
case .runtime:
return L10n.runtime
case .officialRating:
return L10n.officialRating
}
}
}
27 changes: 27 additions & 0 deletions Shared/Extensions/JellyfinAPI/Video3DFormat.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// 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) 2024 Jellyfin & Jellyfin Contributors
//

import Foundation
import JellyfinAPI

extension Video3DFormat {
var displayTitle: String {
switch self {
case .halfSideBySide:
return L10n.halfSideBySide
case .fullSideBySide:
return L10n.fullSideBySide
case .fullTopAndBottom:
return L10n.fullTopAndBottom
case .halfTopAndBottom:
return L10n.halfTopAndBottom
case .mvc:
return L10n.mvc
}
}
}
28 changes: 28 additions & 0 deletions Shared/Objects/DisplayOrder/BoxSetDisplayOrder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// 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) 2024 Jellyfin & Jellyfin Contributors
//

import Foundation

enum BoxSetDisplayOrder: String, CaseIterable, Identifiable {
case dateModified = "DateModified"
case sortName = "SortName"
case premiereDate = "PremiereDate"

var id: String { rawValue }

var displayTitle: String {
switch self {
case .dateModified:
return L10n.dateModified
case .sortName:
return L10n.sortName
case .premiereDate:
return L10n.premiereDate
}
}
}
52 changes: 52 additions & 0 deletions Shared/Objects/DisplayOrder/SeriesDisplayOrder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// 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) 2024 Jellyfin & Jellyfin Contributors
//

import Foundation

enum SeriesDisplayOrder: String, CaseIterable, Identifiable {
case aired = "Aired"
case originalAirDate
case absolute
case dvd
case digital
case storyArc
case production
case tv
case alternate
case regional
case alternateDVD = "altdvd"

var id: String { rawValue }

var displayTitle: String {
switch self {
case .aired:
return L10n.aired
case .originalAirDate:
return L10n.originalAirDate
case .absolute:
return L10n.absolute
case .dvd:
return L10n.dvd
case .digital:
return L10n.digital
case .storyArc:
return L10n.storyArc
case .production:
return L10n.production
case .tv:
return L10n.tv
case .alternate:
return L10n.alternate
case .regional:
return L10n.regional
case .alternateDVD:
return L10n.alternateDVD
}
}
}
26 changes: 26 additions & 0 deletions Shared/Objects/SeriesStatus.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// 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) 2024 Jellyfin & Jellyfin Contributors
//

import Foundation

enum SeriesStatus: String, CaseIterable {
case continuing = "Continuing"
case ended = "Ended"
case unreleased = "Unreleased"

var displayTitle: String {
switch self {
case .continuing:
return L10n.continuing
case .ended:
return L10n.ended
case .unreleased:
return L10n.unreleased
}
}
}
Loading