[tvOS] ItemView Button Cleanup (#1296)

This commit is contained in:
Joe 2024-11-11 20:24:32 -07:00 committed by GitHub
parent 83843e2173
commit d276bd7449
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 182 additions and 67 deletions

View File

@ -1,61 +0,0 @@
//
// 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 ItemView {
struct ActionButtonHStack: View {
@ObservedObject
var viewModel: ItemViewModel
var body: some View {
HStack {
Button {
viewModel.send(.toggleIsPlayed)
} label: {
Group {
if viewModel.item.userData?.isPlayed ?? false {
Image(systemName: "checkmark.circle.fill")
.symbolRenderingMode(.palette)
.foregroundStyle(
.primary,
Color.jellyfinPurple
)
} else {
Image(systemName: "checkmark.circle")
}
}
.font(.title3)
.frame(height: 100)
.frame(maxWidth: .infinity)
}
.buttonStyle(.plain)
Button {
viewModel.send(.toggleIsFavorite)
} label: {
Group {
if viewModel.item.userData?.isFavorite ?? false {
Image(systemName: "heart.circle.fill")
.symbolRenderingMode(.palette)
.foregroundStyle(.white, .pink)
} else {
Image(systemName: "heart.circle")
}
}
.font(.title3)
.frame(height: 100)
.frame(maxWidth: .infinity)
}
.buttonStyle(.plain)
}
}
}
}

View File

@ -0,0 +1,53 @@
//
// 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 Defaults
import SwiftUI
extension ItemView {
struct ActionButton: View {
@Environment(\.isSelected)
private var isSelected
@FocusState
private var isFocused: Bool
let title: String
let icon: String
let selectedIcon: String
let onSelect: () -> Void
// MARK: - Body
var body: some View {
Button(action: onSelect) {
ZStack {
if isSelected {
Rectangle()
.fill(
isFocused ? AnyShapeStyle(HierarchicalShapeStyle.primary) :
AnyShapeStyle(HierarchicalShapeStyle.primary.opacity(0.5))
)
} else {
Rectangle()
.fill(isFocused ? Color.white : Color.white.opacity(0.5))
}
Label(title, systemImage: isSelected ? selectedIcon : icon)
.font(.title3)
.fontWeight(.semibold)
.foregroundStyle(.black)
.labelStyle(.iconOnly)
}
}
.focused($isFocused)
.buttonStyle(.card)
}
}
}

View File

@ -0,0 +1,57 @@
//
// 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 ItemView {
struct ActionButtonHStack: View {
@ObservedObject
var viewModel: ItemViewModel
// TODO: Shrink to minWWith 100 (button) / 50 (menu) and 16 spacing to get 4 buttons inline
var body: some View {
HStack(alignment: .center, spacing: 24) {
// MARK: - Toggle Played
ActionButton(
title: L10n.played,
icon: "checkmark.circle",
selectedIcon: "checkmark.circle.fill"
) {
viewModel.send(.toggleIsPlayed)
}
.foregroundStyle(.purple)
.environment(\.isSelected, viewModel.item.userData?.isPlayed ?? false)
.frame(minWidth: 140, maxWidth: .infinity)
// MARK: - Toggle Favorite
ActionButton(
title: L10n.favorited,
icon: "heart.circle",
selectedIcon: "heart.circle.fill"
) {
viewModel.send(.toggleIsFavorite)
}
.foregroundStyle(.pink)
.environment(\.isSelected, viewModel.item.userData?.isFavorite ?? false)
.frame(minWidth: 140, maxWidth: .infinity)
// MARK: - Additional Menu Options
// TODO: Enable if there are more items needed
/* ActionMenu {}
.frame(width: 70)*/
}
.frame(height: 100)
}
}
}

View File

@ -0,0 +1,50 @@
//
// 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 Defaults
import SwiftUI
extension ItemView {
struct ActionMenu<Content: View>: View {
@FocusState
private var isFocused: Bool
@ViewBuilder
let menuItems: Content
// MARK: - Body
var body: some View {
Menu {
menuItems
} label: {
ZStack {
RoundedRectangle(cornerRadius: 10)
.fill(isFocused ? Color.white : Color.white.opacity(0.5))
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.clear, lineWidth: 2)
)
Label(L10n.menuButtons, systemImage: "ellipsis")
.font(.title3)
.fontWeight(.semibold)
.foregroundStyle(.black)
.labelStyle(.iconOnly)
.rotationEffect(.degrees(90))
}
}
.focused($isFocused)
.scaleEffect(isFocused ? 1.20 : 1.0)
.animation(.easeInOut(duration: 0.15), value: isFocused)
.menuStyle(.borderlessButton)
}
}
}

View File

@ -37,7 +37,7 @@ extension SeriesEpisodeSelector {
Image(systemName: "play.fill")
.resizable()
.frame(width: 50, height: 50)
.foregroundColor(.white)
.foregroundStyle(.secondary)
}
}
}

View File

@ -52,20 +52,19 @@ extension SeriesEpisodeSelector {
Button {
onSelect()
} label: {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 8) {
subHeaderView
headerView
contentView
// Removing the alignment below makes the text center
.frame(maxWidth: .infinity, alignment: .leading)
L10n.seeMore.text
.font(.caption.weight(.light))
.foregroundStyle(accentColor)
}
.padding(5)
.padding()
}
.buttonStyle(.card)
}

View File

@ -41,6 +41,7 @@ extension SeriesEpisodeSelector {
) { episode in
SeriesEpisodeSelector.EpisodeCard(episode: episode)
.focused($focusedEpisodeID, equals: episode.id)
.padding(.horizontal, 4)
}
.scrollBehavior(.continuousLeadingEdge)
.insets(horizontal: EdgeInsets.edgePadding)

View File

@ -59,6 +59,7 @@
4E35CE6D2CBEDB7600DBD886 /* TaskState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E35CE6B2CBEDB7300DBD886 /* TaskState.swift */; };
4E36395C2CC4DF0E00110EBC /* APIKeysViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E36395A2CC4DF0900110EBC /* APIKeysViewModel.swift */; };
4E4A53222CBE0A1C003BD24D /* ChevronAlertButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB7B33A2CBDE63F004A342E /* ChevronAlertButton.swift */; };
4E5334A22CD1A28700D59FA8 /* ActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E5334A12CD1A28400D59FA8 /* ActionButton.swift */; };
4E5E48E52AB59806003F1B48 /* CustomizeViewsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */; };
4E63B9FA2C8A5BEF00C25378 /* AdminDashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E63B9F42C8A5BEF00C25378 /* AdminDashboardView.swift */; };
4E63B9FC2C8A5C3E00C25378 /* ActiveSessionsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E63B9FB2C8A5C3E00C25378 /* ActiveSessionsViewModel.swift */; };
@ -121,6 +122,7 @@
4EF18B262CB9934C00343666 /* LibraryRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EF18B252CB9934700343666 /* LibraryRow.swift */; };
4EF18B282CB9936D00343666 /* ListColumnsPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EF18B272CB9936400343666 /* ListColumnsPickerView.swift */; };
4EF18B2A2CB993BD00343666 /* ListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EF18B292CB993AD00343666 /* ListRow.swift */; };
4EF659E32CDD270D00E0BE5D /* ActionMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EF659E22CDD270B00E0BE5D /* ActionMenu.swift */; };
531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690E6267ABD79005D8AB9 /* HomeView.swift */; };
531AC8BF26750DE20091C7EB /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531AC8BE26750DE20091C7EB /* ImageView.swift */; };
5321753B2671BCFC005491E6 /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5321753A2671BCFC005491E6 /* SettingsViewModel.swift */; };
@ -1099,6 +1101,7 @@
4E35CE682CBED95F00DBD886 /* DayOfWeek.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayOfWeek.swift; sourceTree = "<group>"; };
4E35CE6B2CBEDB7300DBD886 /* TaskState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskState.swift; sourceTree = "<group>"; };
4E36395A2CC4DF0900110EBC /* APIKeysViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIKeysViewModel.swift; sourceTree = "<group>"; };
4E5334A12CD1A28400D59FA8 /* ActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButton.swift; sourceTree = "<group>"; };
4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeViewsSettings.swift; sourceTree = "<group>"; };
4E63B9F42C8A5BEF00C25378 /* AdminDashboardView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdminDashboardView.swift; sourceTree = "<group>"; };
4E63B9FB2C8A5C3E00C25378 /* ActiveSessionsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActiveSessionsViewModel.swift; sourceTree = "<group>"; };
@ -1152,6 +1155,7 @@
4EF18B252CB9934700343666 /* LibraryRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryRow.swift; sourceTree = "<group>"; };
4EF18B272CB9936400343666 /* ListColumnsPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListColumnsPickerView.swift; sourceTree = "<group>"; };
4EF18B292CB993AD00343666 /* ListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRow.swift; sourceTree = "<group>"; };
4EF659E22CDD270B00E0BE5D /* ActionMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionMenu.swift; sourceTree = "<group>"; };
531690E6267ABD79005D8AB9 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
531AC8BE26750DE20091C7EB /* ImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = "<group>"; };
5321753A2671BCFC005491E6 /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
@ -2018,6 +2022,16 @@
path = PlaybackBitrate;
sourceTree = "<group>";
};
4E5334A02CD1A27C00D59FA8 /* ActionButtons */ = {
isa = PBXGroup;
children = (
4E5334A12CD1A28400D59FA8 /* ActionButton.swift */,
E1C926032887565C002A7A66 /* ActionButtonHStack.swift */,
4EF659E22CDD270B00E0BE5D /* ActionMenu.swift */,
);
path = ActionButtons;
sourceTree = "<group>";
};
4E63B9F52C8A5BEF00C25378 /* AdminDashboardView */ = {
isa = PBXGroup;
children = (
@ -3967,7 +3981,7 @@
isa = PBXGroup;
children = (
E1A16CA2288A7D0000EA4679 /* AboutView */,
E1C926032887565C002A7A66 /* ActionButtonHStack.swift */,
4E5334A02CD1A27C00D59FA8 /* ActionButtons */,
E1C926012887565C002A7A66 /* AttributeHStack.swift */,
E185920528CDAA6400326F80 /* CastAndCrewHStack.swift */,
E1153D982BBA3E6100424D36 /* EpisodeSelector */,
@ -4572,6 +4586,7 @@
E152107D2947ACA000375CC2 /* InvertedLightAppIcon.swift in Sources */,
E1549663296CA2EF00C4EF88 /* UserSession.swift in Sources */,
4EF18B2A2CB993BD00343666 /* ListRow.swift in Sources */,
4EF659E32CDD270D00E0BE5D /* ActionMenu.swift in Sources */,
531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */,
E145EB232BDCCA43003BF6F3 /* BulletedList.swift in Sources */,
E104DC972B9E7E29008F506D /* AssertionFailureView.swift in Sources */,
@ -4893,6 +4908,7 @@
E1575E9A293E7B1E001665B1 /* Array.swift in Sources */,
E1575E8D293E7B1E001665B1 /* URLComponents.swift in Sources */,
E187A60329AB28F0008387E6 /* RotateContentView.swift in Sources */,
4E5334A22CD1A28700D59FA8 /* ActionButton.swift in Sources */,
4EBE064E2C7EB6D3004A6C03 /* VideoPlayerType.swift in Sources */,
E1575E94293E7B1E001665B1 /* VerticalAlignment.swift in Sources */,
E1575EA3293E7B1E001665B1 /* UIDevice.swift in Sources */,