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 - Identify Media Item #1369

Merged
merged 20 commits into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions Shared/Coordinators/ItemEditorCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ final class ItemEditorCoordinator: ObservableObject, NavigationCoordinatable {

// MARK: - Route to Metadata

@Route(.push)
var identifyItem = makeIdentifyItem
@Route(.modal)
var editMetadata = makeEditMetadata

Expand Down Expand Up @@ -60,6 +62,11 @@ final class ItemEditorCoordinator: ObservableObject, NavigationCoordinatable {

// MARK: - Item Metadata

@ViewBuilder
func makeIdentifyItem(item: BaseItemDto) -> some View {
IdentifyItemView(item: item)
}

func makeEditMetadata(item: BaseItemDto) -> NavigationViewCoordinator<BasicNavigationViewCoordinator> {
NavigationViewCoordinator {
EditMetadataView(viewModel: ItemEditorViewModel(item: item))
Expand Down
56 changes: 56 additions & 0 deletions Shared/Extensions/JellyfinAPI/RemoteSearchResult.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// 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
import SwiftUI

extension RemoteSearchResult: Displayable {

var displayTitle: String {
name ?? L10n.unknown
}
}

// TODO: fix in SDK, should already be equatable
extension RemoteSearchResult: @retroactive Hashable, @retroactive Identifiable {

public var id: Int {
hashValue
}

public func hash(into hasher: inout Hasher) {
hasher.combine(albumArtist)
hasher.combine(artists)
hasher.combine(imageURL)
hasher.combine(indexNumber)
hasher.combine(indexNumberEnd)
hasher.combine(name)
hasher.combine(overview)
hasher.combine(parentIndexNumber)
hasher.combine(premiereDate)
hasher.combine(productionYear)
hasher.combine(providerIDs)
hasher.combine(searchProviderName)
}

public static func == (lhs: RemoteSearchResult, rhs: RemoteSearchResult) -> Bool {
lhs.albumArtist == rhs.albumArtist &&
lhs.artists == rhs.artists &&
lhs.imageURL == rhs.imageURL &&
lhs.indexNumber == rhs.indexNumber &&
lhs.indexNumberEnd == rhs.indexNumberEnd &&
lhs.name == rhs.name &&
lhs.overview == rhs.overview &&
lhs.parentIndexNumber == rhs.parentIndexNumber &&
lhs.premiereDate == rhs.premiereDate &&
lhs.productionYear == rhs.productionYear &&
lhs.providerIDs == rhs.providerIDs &&
lhs.searchProviderName == rhs.searchProviderName
}
}
4 changes: 4 additions & 0 deletions Shared/Extensions/Optional.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import Foundation

extension Optional where Wrapped: Collection {

var isNilOrEmpty: Bool {
self?.isEmpty ?? true
}

mutating func appendedOrInit(_ element: Wrapped.Element) -> [Wrapped.Element] {
if let self {
return self + [element]
Expand Down
8 changes: 8 additions & 0 deletions Shared/Extensions/String.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ extension String {
self + String(element)
}

func appending(_ element: @autoclosure () -> String, if condition: Bool) -> String {
if condition {
return self + element()
} else {
return self
}
}

func prepending(_ element: String) -> String {
element + self
}
Expand Down
8 changes: 8 additions & 0 deletions Shared/Extensions/URL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ extension URL: Identifiable {
}
}

extension URL {

init?(string: String?) {
guard let string = string else { return nil }
self.init(string: string)
}
}

extension URL {

static var documents: URL {
Expand Down
5 changes: 4 additions & 1 deletion Shared/Services/Notifications.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,15 @@ extension Notifications.Key {

// MARK: - Media Items

// TODO: come up with a cleaner, more defined way for item update notifications

/// - Payload: The new item with updated metadata.
static var itemMetadataDidChange: Key<BaseItemDto> {
Key("itemMetadataDidChange")
}

static var itemShouldRefresh: Key<(itemID: String, parentID: String?)> {
/// - Payload: The ID of the item that should refresh
static var itemShouldRefreshMetadata: Key<String> {
Key("itemShouldRefresh")
}

Expand Down
12 changes: 12 additions & 0 deletions Shared/Strings/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ internal enum L10n {
internal static let appIcon = L10n.tr("Localizable", "appIcon", fallback: "App Icon")
/// Application Name
internal static let applicationName = L10n.tr("Localizable", "applicationName", fallback: "Application Name")
/// Applying media information
internal static let applyingMediaInformation = L10n.tr("Localizable", "applyingMediaInformation", fallback: "Applying media information")
/// Arranger
internal static let arranger = L10n.tr("Localizable", "arranger", fallback: "Arranger")
/// Artist
Expand Down Expand Up @@ -602,6 +604,10 @@ internal enum L10n {
internal static let home = L10n.tr("Localizable", "home", fallback: "Home")
/// Hours
internal static let hours = L10n.tr("Localizable", "hours", fallback: "Hours")
/// ID
internal static let id = L10n.tr("Localizable", "id", fallback: "ID")
/// Identify
internal static let identify = L10n.tr("Localizable", "identify", fallback: "Identify")
/// Idle
internal static let idle = L10n.tr("Localizable", "idle", fallback: "Idle")
/// Illustrator
Expand Down Expand Up @@ -906,6 +912,8 @@ internal enum L10n {
internal static let production = L10n.tr("Localizable", "production", fallback: "Production")
/// Production Locations
internal static let productionLocations = L10n.tr("Localizable", "productionLocations", fallback: "Production Locations")
/// Production Year
internal static let productionYear = L10n.tr("Localizable", "productionYear", fallback: "Production Year")
/// Profile Image
internal static let profileImage = L10n.tr("Localizable", "profileImage", fallback: "Profile Image")
/// Profiles
Expand All @@ -914,6 +922,8 @@ internal enum L10n {
internal static let programs = L10n.tr("Localizable", "programs", fallback: "Programs")
/// Progress
internal static let progress = L10n.tr("Localizable", "progress", fallback: "Progress")
/// Provider
internal static let provider = L10n.tr("Localizable", "provider", fallback: "Provider")
/// Public Users
internal static let publicUsers = L10n.tr("Localizable", "publicUsers", fallback: "Public Users")
/// Quick Connect
Expand Down Expand Up @@ -1298,6 +1308,8 @@ internal enum L10n {
internal static let unsavedChangesMessage = L10n.tr("Localizable", "unsavedChangesMessage", fallback: "You have unsaved changes. Are you sure you want to discard them?")
/// URL
internal static let url = L10n.tr("Localizable", "url", fallback: "URL")
/// Use as item
internal static let useAsItem = L10n.tr("Localizable", "useAsItem", fallback: "Use as item")
/// Use as Transcoding Profile
internal static let useAsTranscodingProfile = L10n.tr("Localizable", "useAsTranscodingProfile", fallback: "Use as Transcoding Profile")
/// Use Primary Image
Expand Down
Loading