[iOS & tvOS] Fix Version Selection (#1429)
Some checks failed
Build 🔨 / Build 🔨 (Swiftfin tvOS) (push) Has been cancelled
Build 🔨 / Build 🔨 (Swiftfin) (push) Has been cancelled

* iOS + tvOS Versioning

* hasMultipleVersions

Co-authored-by: Ethan Pippin <ethanpippin2343@gmail.com>

* v1

* Button Witdh to avoid overflow. Fix build issues.

* Let instead of Var.

* cleanup

---------

Co-authored-by: Ethan Pippin <ethanpippin2343@gmail.com>
This commit is contained in:
Joe Kribs 2025-02-19 22:02:11 -07:00 committed by GitHub
parent c113c341bf
commit 137f0dbf13
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 82 additions and 6 deletions

View File

@ -27,6 +27,7 @@ class ItemViewModel: ViewModel, Stateful {
case replace(BaseItemDto)
case toggleIsFavorite
case toggleIsPlayed
case selectMediaSource(MediaSourceInfo)
}
// MARK: BackgroundState
@ -272,6 +273,11 @@ class ItemViewModel: ViewModel, Stateful {
}
.asAnyCancellable()
return state
case let .selectMediaSource(newSource):
selectedMediaSource = newSource
return state
}
}

View File

@ -56,6 +56,7 @@ extension ItemView {
.labelStyle(.iconOnly)
}
}
.padding(0)
.focused($isFocused)
.buttonStyle(.card)
}

View File

@ -21,7 +21,7 @@ extension ItemView {
var viewModel: ItemViewModel
@StateObject
var deleteViewModel: DeleteItemViewModel
private var deleteViewModel: DeleteItemViewModel
// MARK: - Defaults
@ -73,7 +73,6 @@ extension ItemView {
// MARK: - Body
/// Shrink to minWidth 100 (button) / 50 (menu) and 16 spacing to get 3 buttons + menu
var body: some View {
HStack(alignment: .center, spacing: 24) {
@ -88,7 +87,7 @@ extension ItemView {
}
.foregroundStyle(.purple)
.environment(\.isSelected, viewModel.item.userData?.isPlayed ?? false)
.frame(minWidth: 140, maxWidth: .infinity)
.frame(minWidth: 80, maxWidth: .infinity)
// MARK: - Toggle Favorite
@ -101,7 +100,14 @@ extension ItemView {
}
.foregroundStyle(.pink)
.environment(\.isSelected, viewModel.item.userData?.isFavorite ?? false)
.frame(minWidth: 140, maxWidth: .infinity)
.frame(minWidth: 80, maxWidth: .infinity)
// MARK: - Select Merged Version
if let mediaSources = viewModel.playButtonItem?.mediaSources, mediaSources.count > 1 {
VersionMenu(viewModel: viewModel, mediaSources: mediaSources)
.frame(minWidth: 80, maxWidth: .infinity)
}
// MARK: - Additional Menu Options
@ -118,7 +124,7 @@ extension ItemView {
}
}
}
.frame(width: 70)
.frame(minWidth: 30, maxWidth: 50)
}
}
.frame(height: 100)

View File

@ -0,0 +1,59 @@
//
// 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 SwiftUI
extension ItemView {
struct VersionMenu: View {
// MARK: - Focus State
@FocusState
private var isFocused: Bool
@ObservedObject
var viewModel: ItemViewModel
let mediaSources: [MediaSourceInfo]
// MARK: - Body
var body: some View {
Menu {
ForEach(mediaSources, id: \.hashValue) { mediaSource in
Button {
viewModel.send(.selectMediaSource(mediaSource))
} label: {
if let selectedMediaSource = viewModel.selectedMediaSource, selectedMediaSource == mediaSource {
Label(selectedMediaSource.displayTitle, systemImage: "checkmark")
} else {
Text(mediaSource.displayTitle)
}
}
}
} label: {
ZStack {
RoundedRectangle(cornerRadius: 10)
.fill(isFocused ? Color.white : Color.white.opacity(0.5))
Label(L10n.version, systemImage: "list.dash")
.font(.title3)
.fontWeight(.semibold)
.foregroundStyle(.black)
.labelStyle(.iconOnly)
}
}
.focused($isFocused)
.scaleEffect(isFocused ? 1.20 : 1.0)
.animation(.easeInOut(duration: 0.15), value: isFocused)
.menuStyle(.borderlessButton)
}
}
}

View File

@ -225,6 +225,7 @@
4ECF5D8B2D0A57EF00F066B1 /* DynamicDayOfWeek.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ECF5D892D0A57EF00F066B1 /* DynamicDayOfWeek.swift */; };
4ED25CA12D07E3590010333C /* EditAccessScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ED25CA02D07E3520010333C /* EditAccessScheduleView.swift */; };
4ED25CA42D07E4990010333C /* EditAccessScheduleRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ED25CA22D07E4990010333C /* EditAccessScheduleRow.swift */; };
4EDDB49C2D596E1200DA16E8 /* VersionMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDDB49B2D596E0700DA16E8 /* VersionMenu.swift */; };
4EE07CBB2D08B19700B0B636 /* ErrorMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE07CBA2D08B19100B0B636 /* ErrorMessage.swift */; };
4EE07CBC2D08B19700B0B636 /* ErrorMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE07CBA2D08B19100B0B636 /* ErrorMessage.swift */; };
4EE141692C8BABDF0045B661 /* ActiveSessionProgressSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE141682C8BABDF0045B661 /* ActiveSessionProgressSection.swift */; };
@ -1441,6 +1442,7 @@
4ED25CA02D07E3520010333C /* EditAccessScheduleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAccessScheduleView.swift; sourceTree = "<group>"; };
4ED25CA22D07E4990010333C /* EditAccessScheduleRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAccessScheduleRow.swift; sourceTree = "<group>"; };
4EDBDCD02CBDD6510033D347 /* SessionInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionInfo.swift; sourceTree = "<group>"; };
4EDDB49B2D596E0700DA16E8 /* VersionMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionMenu.swift; sourceTree = "<group>"; };
4EE07CBA2D08B19100B0B636 /* ErrorMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorMessage.swift; sourceTree = "<group>"; };
4EE141682C8BABDF0045B661 /* ActiveSessionProgressSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveSessionProgressSection.swift; sourceTree = "<group>"; };
4EE766F42D131FB7009658F0 /* IdentifyItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentifyItemView.swift; sourceTree = "<group>"; };
@ -2490,6 +2492,7 @@
E1C926032887565C002A7A66 /* ActionButtonHStack.swift */,
4EF659E22CDD270B00E0BE5D /* ActionMenu.swift */,
4E97D1842D064B43004B89AD /* RefreshMetadataButton.swift */,
4EDDB49B2D596E0700DA16E8 /* VersionMenu.swift */,
);
path = ActionButtons;
sourceTree = "<group>";
@ -5971,6 +5974,7 @@
E1CAF6602BA345830087D991 /* MediaViewModel.swift in Sources */,
E19D41A82BEEDC5F0082B8B2 /* UserLocalSecurityViewModel.swift in Sources */,
E111D8FA28D0400900400001 /* PagingLibraryView.swift in Sources */,
4EDDB49C2D596E1200DA16E8 /* VersionMenu.swift in Sources */,
E1EA9F6B28F8A79E00BEC442 /* VideoPlayerManager.swift in Sources */,
BD0BA22F2AD6508C00306A8D /* DownloadVideoPlayerManager.swift in Sources */,
4E0A8FFC2CAF74D20014B047 /* TaskCompletionStatus.swift in Sources */,

View File

@ -80,7 +80,7 @@ extension ItemView {
Menu {
ForEach(mediaSources, id: \.hashValue) { mediaSource in
Button {
// viewModel.selectedMediaSource = mediaSource
viewModel.send(.selectMediaSource(mediaSource))
} label: {
if let selectedMediaSource = viewModel.selectedMediaSource, selectedMediaSource == mediaSource {
Label(selectedMediaSource.displayTitle, systemImage: "checkmark")