Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
aeeb79f
wip
LePips Oct 1, 2025
6e9267e
wip
LePips Oct 1, 2025
1fdf8b6
wip
LePips Oct 5, 2025
256c7dc
wip
LePips Oct 5, 2025
8feb3d3
wip
LePips Oct 6, 2025
e6d39fd
wip
LePips Oct 6, 2025
30702ea
wip
LePips Oct 11, 2025
23908d9
wip
LePips Oct 12, 2025
06c342b
wip
LePips Oct 13, 2025
7cfb192
wip
LePips Oct 13, 2025
c9eea5b
wip
LePips Oct 13, 2025
c761bc9
wip
LePips Oct 13, 2025
7b772ea
wip
LePips Oct 24, 2025
554cef0
wip
LePips Oct 30, 2025
b170157
wip
LePips Nov 14, 2025
32dd9bc
Merge branch 'main' into poster-library-home
LePips Nov 14, 2025
407d0fc
wip
LePips Nov 14, 2025
4861db7
wip
LePips Nov 22, 2025
d80f2f9
Merge branch 'main' into poster-library-home
LePips Nov 22, 2025
7180357
fix merge
LePips Nov 22, 2025
c4f421d
wip
LePips Nov 27, 2025
d563dd1
wip
LePips Dec 14, 2025
4f93213
Merge branch 'main' into poster-library-home
LePips Dec 14, 2025
b5d03f8
wip
LePips Dec 20, 2025
4a4d1fe
Merge branch 'main' into poster-library-home
LePips Dec 20, 2025
a648812
wip
LePips Dec 20, 2025
8da8ea7
wip
LePips Dec 20, 2025
17b8c6d
Merge branch 'main' into poster-library-home
LePips Dec 20, 2025
7b03bfd
wip
LePips Dec 20, 2025
ab053ad
Merge branch 'main' into poster-library-home
LePips Dec 20, 2025
6a7dc58
wip
LePips Dec 20, 2025
45e0b3a
wip
LePips Dec 20, 2025
6fe62d9
wip
LePips Dec 20, 2025
5c31fbd
wip
LePips Dec 24, 2025
69f5941
wip
LePips Dec 27, 2025
67664e7
wip
LePips Dec 29, 2025
2961b94
wip
LePips Dec 29, 2025
f1305c7
wip
LePips Dec 29, 2025
f65af00
wip
LePips Dec 29, 2025
f816106
wip
LePips Dec 29, 2025
b048b63
wip
LePips Dec 31, 2025
5fb7c0f
wip
LePips Dec 31, 2025
45a5bbf
wip
LePips Dec 31, 2025
a7ab208
wip
LePips Dec 31, 2025
d0a6d7a
wip
LePips Jan 2, 2026
aa134a7
Merge branch 'main' into poster-library-home
LePips Jan 2, 2026
719acc0
wip
LePips Jan 2, 2026
ff656bf
wip
LePips Jan 4, 2026
c8425a7
wip
LePips Jan 10, 2026
51fa5dc
wip
LePips Jan 13, 2026
f105500
wip
LePips Jan 15, 2026
f465963
wip
LePips Jan 17, 2026
24718cd
wip
LePips Feb 7, 2026
8527269
Merge branch 'main' into poster-library-home
LePips Feb 28, 2026
141b3f6
wip
LePips Feb 28, 2026
3643a58
wip
LePips Feb 28, 2026
839cf21
WIP before merging main
LePips May 7, 2026
e7bae1d
Merge main into poster-library-home
LePips May 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,4 @@ XcodeConfig/DevelopmentTeam.xcconfig

# Other
.idea
.code
86 changes: 86 additions & 0 deletions Shared/Components/ActionButtonHStack.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//
// 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) 2026 Jellyfin & Jellyfin Contributors
//

import Defaults
import Factory
import JellyfinAPI
import SwiftUI

// TODO: find some other name

struct ActionButtonHStack: View {

@Injected(\.itemUserDataHandler)
private var itemUserDataHandler

@StoredValue(.User.enabledTrailers)
private var enabledTrailers: TrailerSelection

let item: BaseItemDto
let localTrailers: [BaseItemDto]

var body: some View {
HStack(alignment: .center, spacing: 10) {

if item.canBePlayed {

let isPlayedSelected = item.userData?.isPlayed == true

Button(L10n.played, systemImage: "checkmark") {
itemUserDataHandler.setPlayedStatus(for: item, isPlayed: !isPlayedSelected)
}
.foregroundStyle(.white, Color.jellyfinPurple)
.isHighlighted(isPlayedSelected)
.frame(maxWidth: .infinity)
}

let isFavoriteSelected = item.userData?.isFavorite == true

Button(L10n.favorite, systemImage: isFavoriteSelected ? "heart.fill" : "heart") {
itemUserDataHandler.setFavoriteStatus(for: item, isFavorited: !isFavoriteSelected)
}
.foregroundStyle(.white, .red)
.isHighlighted(isFavoriteSelected)
.frame(maxWidth: .infinity)

TrailerMenu(
localTrailers: enabledTrailers.contains(.local) ? localTrailers : [],
remoteTrailers: enabledTrailers.contains(.external) ? (item.remoteTrailers ?? []) : []
)
.menuStyle(.button)
.foregroundStyle(.white)
.isHighlighted(false)
.frame(maxWidth: .infinity)

if UIDevice.isTV {
Menu {
Section {
Button(L10n.delete) {}
Button(L10n.edit) {}
}
} label: {
Label {
Text(L10n.advanced)
} icon: {
Image(systemName: "ellipsis")
.rotationEffect(.degrees(90))
}
}
.menuStyle(.button)
.foregroundStyle(.black, .white)
.frame(width: 60)
}
}
.font(.title3)
.fontWeight(.semibold)
.labelStyle(ActionLabelStyle())
.buttonStyle(_BasicHoverButtonStyle())
.frame(height: UIDevice.isTV ? 100 : 44)
.withViewContext(.isOverComplexContent)
}
}
16 changes: 3 additions & 13 deletions Shared/Components/AlternateLayoutView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ import SwiftUI
struct AlternateLayoutView<Content: View, Layout: View>: View {

@State
private var layoutSize: CGSize = .zero
private var layoutFrame: CGRect = .zero

private let alignment: Alignment
private let content: (CGSize) -> Content
private let layout: Layout

private let passLayoutSize: Bool

init(
alignment: Alignment = .center,
@ViewBuilder layout: @escaping () -> Layout,
Expand All @@ -28,8 +26,6 @@ struct AlternateLayoutView<Content: View, Layout: View>: View {
self.alignment = alignment
self.content = { _ in content() }
self.layout = layout()

self.passLayoutSize = false
}

init(
Expand All @@ -40,20 +36,14 @@ struct AlternateLayoutView<Content: View, Layout: View>: View {
self.alignment = alignment
self.content = content
self.layout = layout()

self.passLayoutSize = true
}

var body: some View {
layout
.hidden()
.trackingSize($layoutSize)
.trackingFrame($layoutFrame)
.overlay(alignment: alignment) {
if passLayoutSize {
content(layoutSize)
} else {
content(.zero)
}
content(layoutFrame.size)
}
}
}
118 changes: 17 additions & 101 deletions Shared/Components/AttributeBadge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import SwiftUI

struct AttributeBadge: View {
struct AttributeBadge<Content: View>: View {

@Environment(\.font)
private var font
Expand All @@ -19,131 +19,47 @@ struct AttributeBadge: View {
}

private let style: Style
private let content: () -> any View
private let content: Content

private var usedFont: Font {
init(
style: Style,
@ViewBuilder content: () -> Content
) {
self.style = style
self.content = content()
}

private var resolvedFont: Font {
font ?? .caption.weight(.semibold)
}

@ViewBuilder
private var innerBody: some View {
if style == .fill {
content()
.eraseToAnyView()
content
.padding(.init(vertical: 1, horizontal: 4))
.hidden()
.background {
Color(UIColor.lightGray)
.cornerRadius(2)
RoundedRectangle(cornerRadius: 2)
.inverseMask {
content()
.eraseToAnyView()
content
.padding(.init(vertical: 1, horizontal: 4))
}
}
} else {
content()
.eraseToAnyView()
.foregroundStyle(Color(UIColor.lightGray))
content
.padding(.init(vertical: 1, horizontal: 4))
.overlay(
RoundedRectangle(cornerRadius: 2)
.stroke(Color(UIColor.lightGray), lineWidth: 1)
.stroke(lineWidth: 1)
)
}
}

var body: some View {
innerBody
.labelStyle(AttributeBadgeLabelStyle())
.font(usedFont)
}
}

extension AttributeBadge {

init(
style: Style,
title: @autoclosure @escaping () -> Text
) {
self.init(style: style) {
title()
}
}

init(
style: Style,
title: String
) {
self.init(style: style) {
Text(title)
}
}

init(
style: Style,
title: String,
image: Image
) {
self.style = style
self.content = {
Label { Text(title) } icon: { image }
}
}

init(
style: Style,
title: String,
image: @escaping () -> Image
) {
self.style = style
self.content = {
Label { Text(title) } icon: { image() }
}
}

init(
style: Style,
title: String,
systemName: String
) {
self.style = style
self.content = {
Label { Text(title) } icon: { Image(systemName: systemName) }
}
}

init(
style: Style,
title: Text,
image: Image
) {
self.style = style
self.content = {
Label { title } icon: { image }
}
}

init(
style: Style,
title: Text,
image: @escaping () -> Image
) {
self.style = style
self.content = {
Label { title } icon: { image() }
}
}

init(
style: Style,
title: Text,
systemName: String
) {
self.style = style
self.content = {
Label { title } icon: { Image(systemName: systemName) }
}
.font(resolvedFont)
}
}

Expand Down
Loading