Some Polish (#1058)
This commit is contained in:
parent
957022e09d
commit
66c26553ad
|
@ -11,6 +11,8 @@ import SwiftUI
|
|||
|
||||
// TODO: only allow `view` selection when truncated?
|
||||
// TODO: fix when also using `lineLimit(reserveSpace > 1)`
|
||||
// TODO: some false positives for showing see more?
|
||||
// TODO: allow removing empty lines
|
||||
|
||||
struct TruncatedText: View {
|
||||
|
||||
|
|
|
@ -288,7 +288,7 @@
|
|||
E12186DE2718F1C50010884C /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = E12186DD2718F1C50010884C /* Defaults */; };
|
||||
E122A9132788EAAD0060FA63 /* MediaStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = E122A9122788EAAD0060FA63 /* MediaStream.swift */; };
|
||||
E122A9142788EAAD0060FA63 /* MediaStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = E122A9122788EAAD0060FA63 /* MediaStream.swift */; };
|
||||
E12376AE2A33D680001F5B44 /* AboutViewCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12376AD2A33D680001F5B44 /* AboutViewCard.swift */; };
|
||||
E12376AE2A33D680001F5B44 /* AboutView+Card.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12376AD2A33D680001F5B44 /* AboutView+Card.swift */; };
|
||||
E12376B02A33D6AE001F5B44 /* AboutViewCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12376AF2A33D6AE001F5B44 /* AboutViewCard.swift */; };
|
||||
E12376B12A33DB33001F5B44 /* MediaSourceInfoCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E170D106294D23BA0017224C /* MediaSourceInfoCoordinator.swift */; };
|
||||
E12376B32A33DFAC001F5B44 /* ItemOverviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12376B22A33DFAC001F5B44 /* ItemOverviewView.swift */; };
|
||||
|
@ -1115,7 +1115,7 @@
|
|||
E11CEB8F28999D84003E74C7 /* EpisodeItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EpisodeItemView.swift; sourceTree = "<group>"; };
|
||||
E11CEB9328999D9E003E74C7 /* EpisodeItemContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EpisodeItemContentView.swift; sourceTree = "<group>"; };
|
||||
E122A9122788EAAD0060FA63 /* MediaStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaStream.swift; sourceTree = "<group>"; };
|
||||
E12376AD2A33D680001F5B44 /* AboutViewCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewCard.swift; sourceTree = "<group>"; };
|
||||
E12376AD2A33D680001F5B44 /* AboutView+Card.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AboutView+Card.swift"; sourceTree = "<group>"; };
|
||||
E12376AF2A33D6AE001F5B44 /* AboutViewCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewCard.swift; sourceTree = "<group>"; };
|
||||
E12376B22A33DFAC001F5B44 /* ItemOverviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemOverviewView.swift; sourceTree = "<group>"; };
|
||||
E129428428F080B500796AC6 /* OnReceiveNotificationModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnReceiveNotificationModifier.swift; sourceTree = "<group>"; };
|
||||
|
@ -2879,7 +2879,7 @@
|
|||
E18ACA932A15A3D800BB4F35 /* Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E12376AD2A33D680001F5B44 /* AboutViewCard.swift */,
|
||||
E12376AD2A33D680001F5B44 /* AboutView+Card.swift */,
|
||||
E1E750662A33E9B400B2C1EE /* MediaSourcesCard.swift */,
|
||||
E1E750652A33E9B400B2C1EE /* OverviewCard.swift */,
|
||||
E1E750672A33E9B400B2C1EE /* RatingsCard.swift */,
|
||||
|
@ -4393,7 +4393,7 @@
|
|||
E1A1529028FD23D600600579 /* PlaybackSettingsCoordinator.swift in Sources */,
|
||||
E11042752B8013DF00821020 /* Stateful.swift in Sources */,
|
||||
E1AA331F2782639D00F6439C /* OverlayType.swift in Sources */,
|
||||
E12376AE2A33D680001F5B44 /* AboutViewCard.swift in Sources */,
|
||||
E12376AE2A33D680001F5B44 /* AboutView+Card.swift in Sources */,
|
||||
E1A2C154279A7D5A005EC829 /* UIApplication.swift in Sources */,
|
||||
E1D8428F2933F2D900D1041A /* MediaSourceInfo.swift in Sources */,
|
||||
E1BDF2EC2952290200CC0294 /* AspectFillActionButton.swift in Sources */,
|
||||
|
|
|
@ -45,6 +45,11 @@ struct SwiftfinApp: App {
|
|||
|
||||
// Sometimes the tab bar won't appear properly on push, always have material background
|
||||
UITabBar.appearance().scrollEdgeAppearance = UITabBarAppearance(idiom: .unspecified)
|
||||
|
||||
// don't keep last user id
|
||||
if Defaults[.signOutOnClose] {
|
||||
Defaults[.lastSignedInUserID] = nil
|
||||
}
|
||||
}
|
||||
|
||||
var body: some Scene {
|
||||
|
|
|
@ -6,22 +6,112 @@
|
|||
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import CollectionHStack
|
||||
import Defaults
|
||||
import JellyfinAPI
|
||||
import OrderedCollections
|
||||
import SwiftUI
|
||||
|
||||
// TODO: rename `AboutItemView`
|
||||
// TODO: see what to do about bottom padding
|
||||
// - don't like it adds more than the edge
|
||||
// - just have this determine bottom padding
|
||||
// instead of scrollviews?
|
||||
|
||||
extension ItemView {
|
||||
|
||||
struct AboutView: View {
|
||||
|
||||
private enum AboutViewItem: Hashable {
|
||||
|
||||
case image
|
||||
case overview
|
||||
case mediaSource(MediaSourceInfo)
|
||||
case ratings
|
||||
}
|
||||
|
||||
@Default(.accentColor)
|
||||
private var accentColor
|
||||
|
||||
@ObservedObject
|
||||
var viewModel: ItemViewModel
|
||||
|
||||
@State
|
||||
private var contentSize: CGSize = .zero
|
||||
@State
|
||||
private var items: OrderedSet<AboutViewItem>
|
||||
|
||||
init(viewModel: ItemViewModel) {
|
||||
self.viewModel = viewModel
|
||||
|
||||
var items: OrderedSet<AboutViewItem> = [
|
||||
.image,
|
||||
.overview,
|
||||
]
|
||||
|
||||
if let mediaSources = viewModel.item.mediaSources {
|
||||
items.append(contentsOf: mediaSources.map { AboutViewItem.mediaSource($0) })
|
||||
}
|
||||
|
||||
if viewModel.item.hasRatings {
|
||||
items.append(.ratings)
|
||||
}
|
||||
|
||||
self._items = State(initialValue: items)
|
||||
}
|
||||
|
||||
// TODO: break out into a general solution for general use?
|
||||
// use similar math from CollectionHStack
|
||||
private var padImageWidth: CGFloat {
|
||||
let portraitMinWidth: CGFloat = 140
|
||||
let contentWidth = contentSize.width
|
||||
let usableWidth = contentWidth - EdgeInsets.edgePadding * 2
|
||||
var columns = CGFloat(Int(usableWidth / portraitMinWidth))
|
||||
let preItemSpacing = (columns - 1) * (EdgeInsets.edgePadding / 2)
|
||||
let preTotalNegative = EdgeInsets.edgePadding * 2 + preItemSpacing
|
||||
|
||||
if columns * portraitMinWidth + preTotalNegative > contentWidth {
|
||||
columns -= 1
|
||||
}
|
||||
|
||||
let itemSpacing = (columns - 1) * (EdgeInsets.edgePadding / 2)
|
||||
let totalNegative = EdgeInsets.edgePadding * 2 + itemSpacing
|
||||
let itemWidth = (contentWidth - totalNegative) / columns
|
||||
|
||||
return max(0, itemWidth)
|
||||
}
|
||||
|
||||
private var phoneImageWidth: CGFloat {
|
||||
let contentWidth = contentSize.width
|
||||
let usableWidth = contentWidth - EdgeInsets.edgePadding * 2
|
||||
let itemSpacing = (EdgeInsets.edgePadding / 2) * 2
|
||||
let itemWidth = (usableWidth - itemSpacing) / 3
|
||||
|
||||
return max(0, itemWidth)
|
||||
}
|
||||
|
||||
private var cardSize: CGSize {
|
||||
let height = UIDevice.isPad ? padImageWidth * 3 / 2 : phoneImageWidth * 3 / 2
|
||||
let width = height * 1.65
|
||||
|
||||
return CGSize(width: width, height: height)
|
||||
}
|
||||
|
||||
private var imageView: some View {
|
||||
ZStack {
|
||||
Color.clear
|
||||
|
||||
ImageView(
|
||||
viewModel.item.type == .episode ? viewModel.item.seriesImageSource(.primary, maxWidth: 300) : viewModel
|
||||
.item.imageSource(.primary, maxWidth: 300)
|
||||
)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
}
|
||||
.posterStyle(.portrait)
|
||||
.posterShadow()
|
||||
.frame(width: UIDevice.isPad ? padImageWidth : phoneImageWidth)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
L10n.about.text
|
||||
|
@ -30,30 +120,30 @@ extension ItemView {
|
|||
.accessibility(addTraits: [.isHeader])
|
||||
.edgePadding(.horizontal)
|
||||
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack {
|
||||
ImageView(
|
||||
viewModel.item.type == .episode ? viewModel.item.seriesImageSource(.primary, maxWidth: 300) : viewModel
|
||||
.item.imageSource(.primary, maxWidth: 300)
|
||||
)
|
||||
.posterStyle(.portrait)
|
||||
.posterShadow()
|
||||
.frame(width: 130)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
||||
CollectionHStack($items, variadicWidths: true) { item in
|
||||
switch item {
|
||||
case .image:
|
||||
imageView
|
||||
case .overview:
|
||||
OverviewCard(item: viewModel.item)
|
||||
|
||||
if let mediaSources = viewModel.item.mediaSources {
|
||||
ForEach(mediaSources) { source in
|
||||
MediaSourcesCard(subtitle: mediaSources.count > 1 ? source.displayTitle : nil, source: source)
|
||||
}
|
||||
}
|
||||
|
||||
.frame(width: cardSize.width, height: cardSize.height)
|
||||
case let .mediaSource(source):
|
||||
MediaSourcesCard(
|
||||
subtitle: (viewModel.item.mediaSources ?? []).count > 1 ? source.displayTitle : nil,
|
||||
source: source
|
||||
)
|
||||
.frame(width: cardSize.width, height: cardSize.height)
|
||||
case .ratings:
|
||||
RatingsCard(item: viewModel.item)
|
||||
.frame(width: cardSize.width, height: cardSize.height)
|
||||
}
|
||||
.edgePadding(.horizontal)
|
||||
}
|
||||
.clipsToBounds(false)
|
||||
.insets(horizontal: EdgeInsets.edgePadding)
|
||||
.itemSpacing(EdgeInsets.edgePadding / 2)
|
||||
.scrollBehavior(.continuousLeadingEdge)
|
||||
}
|
||||
.trackingSize($contentSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,8 @@ extension ItemView.AboutView {
|
|||
} label: {
|
||||
ZStack(alignment: .leading) {
|
||||
|
||||
Color.secondarySystemFill
|
||||
.cornerRadius(10)
|
||||
Color.systemFill
|
||||
.cornerRadius(ratio: 1 / 45, of: \.height)
|
||||
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
Text(title)
|
||||
|
@ -48,7 +48,6 @@ extension ItemView.AboutView {
|
|||
}
|
||||
.padding()
|
||||
}
|
||||
.frame(width: 330, height: 195)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
|
@ -50,7 +50,7 @@ extension ItemView {
|
|||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
ScrollView(showsIndicators: false) {
|
||||
VStack(spacing: 0) {
|
||||
overlay()
|
||||
.frame(height: (size.height + safeAreaInsets.vertical) * heightRatio)
|
||||
|
|
|
@ -16,7 +16,7 @@ struct CollectionItemView: View {
|
|||
var viewModel: CollectionItemViewModel
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
ScrollView(showsIndicators: false) {
|
||||
ContentView(viewModel: viewModel)
|
||||
.edgePadding(.bottom)
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ struct EpisodeItemView: View {
|
|||
var viewModel: EpisodeItemViewModel
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
ScrollView(showsIndicators: false) {
|
||||
ContentView(viewModel: viewModel)
|
||||
.edgePadding(.bottom)
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ extension ItemView {
|
|||
ImageView(viewModel.item.imageSource(.backdrop, maxWidth: 1920))
|
||||
}
|
||||
}
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.aspectRatio(1.77, contentMode: .fill)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
|
|
Loading…
Reference in New Issue