Skip to content

Commit

Permalink
Merge branch 'jellyfin:main' into iOSSettingsUpdate
Browse files Browse the repository at this point in the history
  • Loading branch information
JPKribs authored Jan 20, 2025
2 parents fb4571d + 553441d commit fd72203
Show file tree
Hide file tree
Showing 54 changed files with 2,943 additions and 365 deletions.
11 changes: 11 additions & 0 deletions Shared/Coordinators/ItemEditorCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ final class ItemEditorCoordinator: ObservableObject, NavigationCoordinatable {
@Route(.modal)
var editMetadata = makeEditMetadata

// MARK: - Route to Images

@Route(.modal)
var editImages = makeEditImages

// MARK: - Route to Genres

@Route(.push)
Expand Down Expand Up @@ -73,6 +78,12 @@ final class ItemEditorCoordinator: ObservableObject, NavigationCoordinatable {
}
}

// MARK: - Item Images

func makeEditImages(viewModel: ItemImagesViewModel) -> NavigationViewCoordinator<ItemImagesCoordinator> {
NavigationViewCoordinator(ItemImagesCoordinator(viewModel: viewModel))
}

// MARK: - Item Genres

@ViewBuilder
Expand Down
52 changes: 52 additions & 0 deletions Shared/Coordinators/ItemImagePickerCoordinator.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) 2025 Jellyfin & Jellyfin Contributors
//

import JellyfinAPI
import Stinsen
import SwiftUI

final class ItemImagePickerCoordinator: NavigationCoordinatable {

// MARK: - Navigation Stack

let stack = Stinsen.NavigationStack(initial: \ItemImagePickerCoordinator.start)

@Root
var start = makeStart

// MARK: - Routes

@Route(.push)
var cropImage = makeCropImage

// MARK: - Observed Object

private let viewModel: ItemImagesViewModel

// MARK: - Image Variable

let type: ImageType

// MARK: - Initializer

init(viewModel: ItemImagesViewModel, type: ImageType) {
self.viewModel = viewModel
self.type = type
}

// MARK: - Crop Image View

func makeCropImage(image: UIImage) -> some View {
ItemPhotoCropView(viewModel: viewModel, image: image, type: type)
}

@ViewBuilder
func makeStart() -> some View {
ItemImagePicker()
}
}
60 changes: 60 additions & 0 deletions Shared/Coordinators/ItemImagesCoordinator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// 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 Factory
import JellyfinAPI
import Stinsen
import SwiftUI

final class ItemImagesCoordinator: ObservableObject, NavigationCoordinatable {

// MARK: - Navigation Stack

let stack = NavigationStack(initial: \ItemImagesCoordinator.start)

@Root
var start = makeStart

private let viewModel: ItemImagesViewModel

// MARK: - Route to Add Remote Image

@Route(.push)
var addImage = makeAddImage

// MARK: - Route to Photo Picker

@Route(.modal)
var photoPicker = makePhotoPicker

// MARK: - Initializer

init(viewModel: ItemImagesViewModel) {
self.viewModel = viewModel
}

// MARK: - Add Remote Images View

@ViewBuilder
func makeAddImage(imageType: ImageType) -> some View {
AddItemImageView(viewModel: viewModel, imageType: imageType)
}

// MARK: - Photo Picker View

func makePhotoPicker(type: ImageType) -> NavigationViewCoordinator<ItemImagePickerCoordinator> {
NavigationViewCoordinator(ItemImagePickerCoordinator(viewModel: self.viewModel, type: type))
}

// MARK: - Start

@ViewBuilder
func makeStart() -> some View {
ItemImagesView(viewModel: self.viewModel)
}
}
14 changes: 3 additions & 11 deletions Shared/Coordinators/UserProfileImageCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import SwiftUI

final class UserProfileImageCoordinator: NavigationCoordinatable {

// MARK: - Navigation Components
// MARK: - Navigation Stack

let stack = Stinsen.NavigationStack(initial: \UserProfileImageCoordinator.start)

Expand All @@ -37,19 +37,11 @@ final class UserProfileImageCoordinator: NavigationCoordinatable {
// MARK: - Views

func makeCropImage(image: UIImage) -> some View {
#if os(iOS)
UserProfileImagePicker.SquareImageCropView(viewModel: viewModel, image: image)
#else
AssertionFailureView("not implemented")
#endif
UserProfileImageCropView(viewModel: viewModel, image: image)
}

@ViewBuilder
func makeStart() -> some View {
#if os(iOS)
UserProfileImagePicker(viewModel: viewModel)
#else
AssertionFailureView("not implemented")
#endif
UserProfileImagePickerView()
}
}
36 changes: 36 additions & 0 deletions Shared/Extensions/JellyfinAPI/ImageInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// 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 Foundation
import JellyfinAPI

extension ImageInfo: @retroactive Identifiable {

public var id: Int {
hashValue
}
}

extension ImageInfo {

func itemImageSource(itemID: String, client: JellyfinClient) -> ImageSource {
let parameters = Paths.GetItemImageParameters(
tag: imageTag,
imageIndex: imageIndex
)
let request = Paths.getItemImage(
itemID: itemID,
imageType: imageType?.rawValue ?? "",
parameters: parameters
)

let itemImageURL = client.fullURL(with: request)

return ImageSource(url: itemImageURL)
}
}
44 changes: 44 additions & 0 deletions Shared/Extensions/JellyfinAPI/ImageType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// 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 Foundation
import JellyfinAPI

extension ImageType: Displayable {

var displayTitle: String {
switch self {
case .primary:
return L10n.primary
case .art:
return L10n.art
case .backdrop:
return L10n.backdrop
case .banner:
return L10n.banner
case .logo:
return L10n.logo
case .thumb:
return L10n.thumb
case .disc:
return L10n.disc
case .box:
return L10n.box
case .screenshot:
return L10n.screenshot
case .menu:
return L10n.menu
case .chapter:
return L10n.chapter
case .boxRear:
return L10n.boxRear
case .profile:
return L10n.profile
}
}
}
34 changes: 34 additions & 0 deletions Shared/Extensions/JellyfinAPI/RemoteImageInfo.swift
Original file line number Diff line number Diff line change
@@ -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
//

import Foundation
import JellyfinAPI
import SwiftUI

extension RemoteImageInfo: @retroactive Identifiable, Poster {

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

var unwrappedIDHashOrZero: Int {
id
}

var subtitle: String? {
language
}

var systemImage: String {
"photo"
}

public var id: Int {
hashValue
}
}
3 changes: 3 additions & 0 deletions Shared/Extensions/Nuke/ImagePipeline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ extension ImagePipeline.Swiftfin {
static let local: ImagePipeline = ImagePipeline(delegate: SwiftfinImagePipelineDelegate()) {
$0.dataCache = DataCache.Swiftfin.local
}

/// An `ImagePipeline` for images to prevent more important images from losing their cache.
static let other: ImagePipeline = ImagePipeline(configuration: .withURLCache)
}

final class SwiftfinImagePipelineDelegate: ImagePipelineDelegate {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@
// Copyright (c) 2025 Jellyfin & Jellyfin Contributors
//

import Foundation
import JellyfinAPI

extension Hashable {
extension RatingType: Displayable {

var hashString: String {
"\(hashValue)"
var displayTitle: String {
switch self {
case .score:
return L10n.score
case .likes:
return L10n.likes
}
}
}
Loading

0 comments on commit fd72203

Please sign in to comment.