Start flattening hierarchy

This commit is contained in:
Aiden Vigue 2021-05-26 11:00:22 -04:00
parent 3cefdb2ad4
commit eb895a0805
No known key found for this signature in database
GPG Key ID: E7570472648F4544
15 changed files with 849 additions and 960 deletions

View File

@ -9,8 +9,8 @@
/* Begin PBXBuildFile section */
5302F82A2658791C00647A2E /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 5302F8292658791C00647A2E /* Sentry */; };
53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */; };
5335256E265E8D5A006CCA86 /* VideoPlayerViewRefactored.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5335256D265E8D5A006CCA86 /* VideoPlayerViewRefactored.swift */; };
5338F74E263B61370014BF09 /* ConnectToServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5338F74D263B61370014BF09 /* ConnectToServerView.swift */; };
5338F751263B62E80014BF09 /* HidingViews in Frameworks */ = {isa = PBXBuildFile; productRef = 5338F750263B62E80014BF09 /* HidingViews */; };
5338F754263B65E10014BF09 /* SwiftyRequest in Frameworks */ = {isa = PBXBuildFile; productRef = 5338F753263B65E10014BF09 /* SwiftyRequest */; };
5338F757263B7E2E0014BF09 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 5338F756263B7E2E0014BF09 /* KeychainSwift */; };
535BAE9F2649E569005FA86D /* ItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535BAE9E2649E569005FA86D /* ItemView.swift */; };
@ -34,7 +34,6 @@
53987CA82657424A00E7EA70 /* EpisodeItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53987CA72657424A00E7EA70 /* EpisodeItemView.swift */; };
539B2DA5263BA5B8007FF1A4 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 539B2DA4263BA5B8007FF1A4 /* SettingsView.swift */; };
53A089D0264DA9DA00D57806 /* MovieItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A089CF264DA9DA00D57806 /* MovieItemView.swift */; };
53D2F74A264C69F6005792BB /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 53D2F749264C69F6005792BB /* Introspect */; };
53D5E3DD264B47EE00BADDC8 /* MobileVLCKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */; };
53D5E3DE264B47EE00BADDC8 /* MobileVLCKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DF641D263D9C0600A7CD1A /* LibraryView.swift */; };
@ -72,6 +71,7 @@
/* Begin PBXFileReference section */
53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceProfileBuilder.swift; sourceTree = "<group>"; };
5335256D265E8D5A006CCA86 /* VideoPlayerViewRefactored.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerViewRefactored.swift; sourceTree = "<group>"; };
5338F74D263B61370014BF09 /* ConnectToServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectToServerView.swift; sourceTree = "<group>"; };
535BAE9E2649E569005FA86D /* ItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemView.swift; sourceTree = "<group>"; };
535BAEA4264A151C005FA86D /* VLCPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VLCPlayer.swift; sourceTree = "<group>"; };
@ -116,9 +116,7 @@
53D5E3DD264B47EE00BADDC8 /* MobileVLCKit.xcframework in Frameworks */,
5338F754263B65E10014BF09 /* SwiftyRequest in Frameworks */,
5302F82A2658791C00647A2E /* Sentry in Frameworks */,
53D2F74A264C69F6005792BB /* Introspect in Frameworks */,
5389277A263CBFE70035E14B /* SwiftyJSON in Frameworks */,
5338F751263B62E80014BF09 /* HidingViews in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -176,6 +174,7 @@
53987CA526572F0700E7EA70 /* SeriesItemView.swift */,
53987CA72657424A00E7EA70 /* EpisodeItemView.swift */,
53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */,
5335256D265E8D5A006CCA86 /* VideoPlayerViewRefactored.swift */,
);
path = JellyfinPlayer;
sourceTree = "<group>";
@ -247,12 +246,10 @@
);
name = JellyfinPlayer;
packageProductDependencies = (
5338F750263B62E80014BF09 /* HidingViews */,
5338F753263B65E10014BF09 /* SwiftyRequest */,
5338F756263B7E2E0014BF09 /* KeychainSwift */,
53892779263CBFE70035E14B /* SwiftyJSON */,
538CD953263E3DC100BB5AF0 /* SDWebImageSwiftUI */,
53D2F749264C69F6005792BB /* Introspect */,
5302F8292658791C00647A2E /* Sentry */,
);
productName = JellyfinPlayer;
@ -286,12 +283,10 @@
);
mainGroup = 5377CBE8263B596A003A4E83;
packageReferences = (
5338F74F263B62E80014BF09 /* XCRemoteSwiftPackageReference "HidingViews" */,
5338F752263B65E10014BF09 /* XCRemoteSwiftPackageReference "SwiftyRequest" */,
5338F755263B7E2E0014BF09 /* XCRemoteSwiftPackageReference "keychain-swift" */,
53892778263CBFE70035E14B /* XCRemoteSwiftPackageReference "SwiftyJSON" */,
538CD952263E3DC100BB5AF0 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */,
53D2F748264C69F6005792BB /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */,
5302F8282658791C00647A2E /* XCRemoteSwiftPackageReference "sentry-cocoa" */,
);
productRefGroup = 5377CBF2263B596A003A4E83 /* Products */;
@ -341,6 +336,7 @@
53987CA82657424A00E7EA70 /* EpisodeItemView.swift in Sources */,
5389277C263CC3DB0035E14B /* BlurHashDecode.swift in Sources */,
53987CA626572F0700E7EA70 /* SeriesItemView.swift in Sources */,
5335256E265E8D5A006CCA86 /* VideoPlayerViewRefactored.swift in Sources */,
539B2DA5263BA5B8007FF1A4 /* SettingsView.swift in Sources */,
AE8C3156265D616A008AA076 /* SettingsViewModel.swift in Sources */,
5338F74E263B61370014BF09 /* ConnectToServerView.swift in Sources */,
@ -557,14 +553,6 @@
minimumVersion = 7.1.0;
};
};
5338F74F263B62E80014BF09 /* XCRemoteSwiftPackageReference "HidingViews" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/GeorgeElsham/HidingViews";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.1.1;
};
};
5338F752263B65E10014BF09 /* XCRemoteSwiftPackageReference "SwiftyRequest" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/Kitura/SwiftyRequest";
@ -597,14 +585,6 @@
minimumVersion = 2.0.2;
};
};
53D2F748264C69F6005792BB /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/siteline/SwiftUI-Introspect";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.1.3;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
@ -613,11 +593,6 @@
package = 5302F8282658791C00647A2E /* XCRemoteSwiftPackageReference "sentry-cocoa" */;
productName = Sentry;
};
5338F750263B62E80014BF09 /* HidingViews */ = {
isa = XCSwiftPackageProductDependency;
package = 5338F74F263B62E80014BF09 /* XCRemoteSwiftPackageReference "HidingViews" */;
productName = HidingViews;
};
5338F753263B65E10014BF09 /* SwiftyRequest */ = {
isa = XCSwiftPackageProductDependency;
package = 5338F752263B65E10014BF09 /* XCRemoteSwiftPackageReference "SwiftyRequest" */;
@ -638,11 +613,6 @@
package = 538CD952263E3DC100BB5AF0 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */;
productName = SDWebImageSwiftUI;
};
53D2F749264C69F6005792BB /* Introspect */ = {
isa = XCSwiftPackageProductDependency;
package = 53D2F748264C69F6005792BB /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */;
productName = Introspect;
};
/* End XCSwiftPackageProductDependency section */
/* Begin XCVersionGroup section */

View File

@ -19,15 +19,6 @@
"version": "5.0.200"
}
},
{
"package": "HidingViews",
"repositoryURL": "https://github.com/GeorgeElsham/HidingViews",
"state": {
"branch": null,
"revision": "7fde89eaeb2f0d3a07f8bf517507c6e27af8e4c3",
"version": "1.1.1"
}
},
{
"package": "KeychainSwift",
"repositoryURL": "https://github.com/evgenyneu/keychain-swift",
@ -109,15 +100,6 @@
"version": "2.13.1"
}
},
{
"package": "Introspect",
"repositoryURL": "https://github.com/siteline/SwiftUI-Introspect",
"state": {
"branch": null,
"revision": "2e09be8af614401bc9f87d40093ec19ce56ccaf2",
"version": "0.1.3"
}
},
{
"package": "SwiftyJSON",
"repositoryURL": "https://github.com/SwiftyJSON/SwiftyJSON",

View File

@ -6,12 +6,11 @@
//
import SwiftUI
import HidingViews
import SwiftyRequest
import SwiftyJSON
import CoreData
import KeychainSwift
import Introspect
import Sentry
import SDWebImageSwiftUI
@ -231,7 +230,9 @@ struct ConnectToServerView: View {
HStack {
Text("Connect")
Spacer()
ProgressView().isHidden(!isWorking)
if(isWorking == true) {
ProgressView()
}
}
}.disabled(isWorking || uri.isEmpty)
}.alert(isPresented: $isErrored) {
@ -252,7 +253,9 @@ struct ConnectToServerView: View {
HStack {
Text("Login")
Spacer()
ProgressView().isHidden(!isWorking)
if(isWorking) {
ProgressView()
}
}
}.disabled(isWorking || username.isEmpty)
.alert(isPresented: $isSignInErrored) {

View File

@ -6,171 +6,13 @@
//
import SwiftUI
import KeychainSwift
import SwiftyRequest
import SwiftyJSON
import Introspect
import Sentry
import SDWebImageSwiftUI
class GlobalData: ObservableObject {
@Published var user: SignedInUser?
@Published var authToken: String = ""
@Published var server: Server?
@Published var authHeader: String = ""
@Published var isInNetwork: Bool = true;
}
extension View {
func withHostingWindow(_ callback: @escaping (UIWindow?) -> Void) -> some View {
self.background(HostingWindowFinder(callback: callback))
}
}
struct HostingWindowFinder: UIViewRepresentable {
var callback: (UIWindow?) -> ()
func makeUIView(context: Context) -> UIView {
let view = UIView()
DispatchQueue.main.async { [weak view] in
callback(view?.window)
}
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
}
}
struct PrefersHomeIndicatorAutoHiddenPreferenceKey: PreferenceKey {
typealias Value = Bool
static var defaultValue: Value = false
static func reduce(value: inout Value, nextValue: () -> Value) {
value = nextValue() || value
}
}
struct ViewPreferenceKey: PreferenceKey {
typealias Value = UIUserInterfaceStyle
static var defaultValue: UIUserInterfaceStyle = .unspecified
static func reduce(value: inout UIUserInterfaceStyle, nextValue: () -> UIUserInterfaceStyle) {
value = nextValue()
}
}
struct SupportedOrientationsPreferenceKey: PreferenceKey {
typealias Value = UIInterfaceOrientationMask
static var defaultValue: UIInterfaceOrientationMask = .allButUpsideDown
static func reduce(value: inout UIInterfaceOrientationMask, nextValue: () -> UIInterfaceOrientationMask) {
// use the most restrictive set from the stack
value.formIntersection(nextValue())
}
}
extension View {
/// Navigate to a new view.
/// - Parameters:
/// - view: View to navigate to.
/// - binding: Only navigates when this condition is `true`.
func navigate<NewView: View>(to view: NewView, when binding: Binding<Bool>) -> some View {
NavigationView {
ZStack {
self
.navigationBarTitle("")
.navigationBarHidden(true)
NavigationLink(
destination: view
.navigationBarTitle("")
.navigationBarHidden(true),
isActive: binding
) {
EmptyView()
}
}
}
}
}
class PreferenceUIHostingController: UIHostingController<AnyView> {
init<V: View>(wrappedView: V) {
let box = Box()
super.init(rootView: AnyView(wrappedView
.onPreferenceChange(PrefersHomeIndicatorAutoHiddenPreferenceKey.self) {
box.value?._prefersHomeIndicatorAutoHidden = $0
}.onPreferenceChange(SupportedOrientationsPreferenceKey.self) {
box.value?._orientations = $0
}.onPreferenceChange(ViewPreferenceKey.self) {
box.value?._viewPreference = $0
}
))
box.value = self
}
@objc required dynamic init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
super.modalPresentationStyle = .fullScreen
}
private class Box {
weak var value: PreferenceUIHostingController?
init() {}
}
// MARK: Prefers Home Indicator Auto Hidden
public var _prefersHomeIndicatorAutoHidden = false {
didSet { setNeedsUpdateOfHomeIndicatorAutoHidden() }
}
override var prefersHomeIndicatorAutoHidden: Bool {
_prefersHomeIndicatorAutoHidden
}
// MARK: Lock orientation
public var _orientations: UIInterfaceOrientationMask = .allButUpsideDown {
didSet {
UIViewController.attemptRotationToDeviceOrientation();
if(_orientations == .landscape) {
let value = UIInterfaceOrientation.landscapeRight.rawValue;
UIDevice.current.setValue(value, forKey: "orientation")
}
}
};
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
_orientations
}
public var _viewPreference: UIUserInterfaceStyle = .unspecified {
didSet {
overrideUserInterfaceStyle = _viewPreference
}
};
}
extension View {
// Controls the application's preferred home indicator auto-hiding when this view is shown.
func prefersHomeIndicatorAutoHidden(_ value: Bool) -> some View {
preference(key: PrefersHomeIndicatorAutoHiddenPreferenceKey.self, value: value)
}
func supportedOrientations(_ supportedOrientations: UIInterfaceOrientationMask) -> some View {
// When rendered, export the requested orientations upward to Root
preference(key: SupportedOrientationsPreferenceKey.self, value: supportedOrientations)
}
func overrideViewPreference(_ viewPreference: UIUserInterfaceStyle) -> some View {
// When rendered, export the requested orientations upward to Root
preference(key: ViewPreferenceKey.self, value: viewPreference)
}
}
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject var orientationInfo: OrientationInfo
@ -327,38 +169,38 @@ struct ContentView: View {
if(needsToSelectServer) {
NavigationView() {
ConnectToServerView(isActive: $needsToSelectServer)
}.environmentObject(globalData).navigationViewStyle(StackNavigationViewStyle())
}
.navigationViewStyle(StackNavigationViewStyle())
.environmentObject(globalData)
} else if(isSignInErrored) {
NavigationView() {
ConnectToServerView(skip_server: true, skip_server_prefill: globalData.server, reauth_deviceId: globalData.user?.device_uuid ?? "", isActive: $isSignInErrored)
}.environmentObject(globalData).navigationViewStyle(StackNavigationViewStyle())
}
.navigationViewStyle(StackNavigationViewStyle())
.environmentObject(globalData)
} else {
if(!jsi.did) {
LoadingView(isShowing: $isLoading) {
TabView(selection: $tabSelection) {
NavigationView() {
VStack {
if(!needsToSelectServer && !isSignInErrored) {
VStack(alignment: .leading) {
ScrollView() {
Spacer().frame(height: orientationInfo.orientation == .portrait ? 0 : 15)
ContinueWatchingView()
NextUpView().padding(EdgeInsets(top: 4, leading: 0, bottom: 0, trailing: 0))
ForEach(librariesShowRecentlyAdded, id: \.self) { library_id in
VStack(alignment: .leading) {
HStack() {
Text("Latest \(library_names[library_id] ?? "")").font(.title2).fontWeight(.bold).padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 16))
Spacer()
NavigationLink(destination: LibraryView(prefill: library_id, names: [library_id: library_names[library_id] ?? ""], libraries: [library_id], filter: "&SortBy=DateCreated&SortOrder=Descending")) {
Text("See All").font(.subheadline).fontWeight(.bold)
}
}.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
LatestMediaView(library: library_id)
}.padding(EdgeInsets(top: 4, leading: 0, bottom: 0, trailing: 0))
}
Spacer().frame(height: 7)
}
NavigationView() {
TabView(selection: $tabSelection) {
VStack(alignment: .leading) {
ScrollView() {
Spacer().frame(height: orientationInfo.orientation == .portrait ? 0 : 15)
ContinueWatchingView()
NextUpView().padding(EdgeInsets(top: 4, leading: 0, bottom: 0, trailing: 0))
ForEach(librariesShowRecentlyAdded, id: \.self) { library_id in
VStack(alignment: .leading) {
HStack() {
Text("Latest \(library_names[library_id] ?? "")").font(.title2).fontWeight(.bold).padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 16))
Spacer()
NavigationLink(destination: LibraryView(prefill: library_id, names: [library_id: library_names[library_id] ?? ""], libraries: [library_id], filter: "&SortBy=DateCreated&SortOrder=Descending")) {
Text("See All").font(.subheadline).fontWeight(.bold)
}
}.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
LatestMediaView(library: library_id)
}.padding(EdgeInsets(top: 4, leading: 0, bottom: 0, trailing: 0))
}
Spacer().frame(height: 7)
}
}
.navigationTitle("Home")
@ -370,32 +212,29 @@ struct ContentView: View {
Image(systemName: "gear")
}
}
}.fullScreenCover( isPresented: $showSettingsPopover) { SettingsView(viewModel: SettingsViewModel(), close: $showSettingsPopover).environmentObject(globalData) }
}.fullScreenCover( isPresented: $showSettingsPopover) { SettingsView(viewModel: SettingsViewModel(), close: $showSettingsPopover) }
.tabItem({
Text("Home")
Image(systemName: "house")
})
.tag("Home")
NavigationView() {
LibraryView(prefill: "", names: library_names, libraries: libraries)
}.navigationViewStyle(StackNavigationViewStyle())
.tabItem({
Text("All Media")
Image(systemName: "folder")
})
.tag("All Media")
}
.navigationViewStyle(StackNavigationViewStyle())
.tabItem({
Text("Home")
Image(systemName: "house")
})
.tag("Home")
NavigationView() {
LibraryView(prefill: "", names: library_names, libraries: libraries)
.navigationTitle("Library")
}
.tabItem({
Text("All Media")
Image(systemName: "folder")
})
.tag("All Media")
}
}.environmentObject(globalData)
.navigationViewStyle(StackNavigationViewStyle())
}
.environmentObject(globalData)
.onAppear(perform: startup)
.navigationViewStyle(StackNavigationViewStyle())
.alert(isPresented: $isNetworkErrored) {
Alert(title: Text("Network Error"), message: Text("Couldn't connect to Jellyfin"), dismissButton: .default(Text("Ok")))
}.introspectTabBarController { (UITabBarController) in
UITabBarController.tabBar.isHidden = false
}
} else {
Text("Signing in...")
@ -410,9 +249,3 @@ struct ContentView: View {
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}

View File

@ -143,7 +143,6 @@ struct ContinueWatchingView: View {
.frame(width: CGFloat((item.ItemProgress/100)*320), height: 7)
.padding(0), alignment: .bottomLeading
)
.shadow(radius: 5)
} else {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=550&quality=80&tag=\(item.Image)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
@ -162,7 +161,6 @@ struct ContinueWatchingView: View {
.frame(width: CGFloat((item.ItemProgress/100)*320), height: 7)
.padding(0), alignment: .bottomLeading
)
.shadow(radius: 5)
}
Text("\(item.Type == "Episode" ? item.SeriesName ?? "" : item.Name)")
.font(.callout)

View File

@ -6,10 +6,8 @@
//
import SwiftUI
import UIKit
import SwiftyRequest
import SwiftyJSON
import Introspect
import SDWebImageSwiftUI
struct EpisodeItemView: View {
@ -136,7 +134,7 @@ struct EpisodeItemView: View {
let imageTag = person["PrimaryImageTag"].string ?? "";
cast.ImageBlurHash = person["ImageBlurHashes"]["Primary"][imageTag].string ?? "";
cast.Role = person["Role"].string ?? "";
cast.Image = URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(cast.Id)/Images/Primary?maxHeight=150&quality=90&tag=\(imageTag)")!
cast.Image = URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(cast.Id)/Images/Primary?maxHeight=250&quality=85&tag=\(imageTag)")!
fullItem.Cast.append(cast);
}
}
@ -191,52 +189,237 @@ struct EpisodeItemView: View {
}
var body: some View {
if(playing) {
VideoPlayerView(item: fullItem, playing: $playing)
.supportedOrientations(.landscape)
.overrideViewPreference(.dark)
.prefersHomeIndicatorAutoHidden(true)
.introspectTabBarController { (UITabBarController) in
UITabBarController.tabBar.isHidden = true
}
} else {
LoadingView(isShowing: $isLoading) {
VStack(alignment:.leading) {
if(!isLoading) {
if(orientationInfo.orientation == .portrait) {
GeometryReader { geometry in
VStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.ParentBackdropItemId)/Images/Backdrop?maxWidth=450&quality=90&tag=\(fullItem.Backdrop)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.BackdropBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.BackdropBlurHash), size: CGSize(width: 32, height: 32))!)
.resizable()
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: UIDevice.current.userInterfaceIdiom == .pad ? 350 : (geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing) * 0.5625)
}
.opacity(0.3)
.aspectRatio(contentMode: .fill)
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: UIDevice.current.userInterfaceIdiom == .pad ? 350 : (geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing) * 0.5625)
.shadow(radius: 5)
.overlay(
LoadingView(isShowing: $isLoading) {
VStack(alignment:.leading) {
if(!isLoading) {
if(orientationInfo.orientation == .portrait) {
GeometryReader { geometry in
VStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.ParentBackdropItemId)/Images/Backdrop?maxWidth=450&quality=90&tag=\(fullItem.Backdrop)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.BackdropBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.BackdropBlurHash), size: CGSize(width: 32, height: 32))!)
.resizable()
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: UIDevice.current.userInterfaceIdiom == .pad ? 350 : (geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing) * 0.5625)
}
.opacity(0.3)
.aspectRatio(contentMode: .fill)
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: UIDevice.current.userInterfaceIdiom == .pad ? 350 : (geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing) * 0.5625)
.shadow(radius: 5)
.overlay(
HStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.SeriesId ?? "")/Images/Primary?maxWidth=250&quality=90&tag=\(fullItem.Poster)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 32, height: 32))!)
.resizable()
.frame(width: 120, height: 180)
.cornerRadius(10)
}.aspectRatio(contentMode: .fill)
.frame(width: 120, height: 180)
.cornerRadius(10)
VStack(alignment: .leading) {
Spacer()
Text(fullItem.Name).font(.headline)
.fontWeight(.semibold)
.foregroundColor(.primary)
.fixedSize(horizontal: false, vertical: true)
.offset(y: -4)
HStack() {
Text(String(fullItem.ProductionYear)).font(.subheadline)
.fontWeight(.medium)
.foregroundColor(.secondary)
.lineLimit(1)
Text(fullItem.Runtime).font(.subheadline)
.fontWeight(.medium)
.foregroundColor(.secondary)
.lineLimit(1)
if(fullItem.OfficialRating != "") {
Text(fullItem.OfficialRating).font(.subheadline)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.lineLimit(1)
.padding(EdgeInsets(top: 1, leading: 4, bottom: 1, trailing: 4))
.overlay(
RoundedRectangle(cornerRadius: 2)
.stroke(Color.secondary, lineWidth: 1)
)
}
if(fullItem.CommunityRating != "0") {
HStack() {
Image(systemName: "star").foregroundColor(.secondary)
Text(fullItem.CommunityRating).font(.subheadline)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.lineLimit(1)
.offset(x: -7, y: 0.7)
}
}
}.frame(maxWidth: .infinity, alignment: .leading)
}.frame(maxWidth: .infinity, alignment: .leading).offset(x: 0, y: UIDevice.current.userInterfaceIdiom == .pad ? -98 : -46).padding(.trailing, 16)
}.offset(x: 16, y: UIDevice.current.userInterfaceIdiom == .pad ? 135 : 40)
, alignment: .bottomLeading)
VStack(alignment: .leading) {
HStack() {
//Play button
NavigationLink(destination: VideoPlayerViewRefactored()) {
HStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.SeriesId ?? "")/Images/Primary?maxWidth=250&quality=90&tag=\(fullItem.Poster)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 32, height: 32))!)
.resizable()
.frame(width: 120, height: 180)
.cornerRadius(10)
}.aspectRatio(contentMode: .fill)
Text(fullItem.Progress == 0 ? "Play" : "\(progressString) left").foregroundColor(Color.white).font(.callout).fontWeight(.semibold)
Image(systemName: "play.fill").foregroundColor(Color.white).font(.system(size: 20))
}
.frame(width: 120, height: 35)
.background(Color(red: 172/255, green: 92/255, blue: 195/255))
.cornerRadius(10)
}
Spacer()
HStack() {
Button() {
favorite.toggle()
} label: {
if(!favorite) {
Image(systemName: "heart").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "heart.fill").foregroundColor(Color(UIColor.systemRed)).font(.system(size: 20))
}
}
Button() {
watched.toggle()
} label: {
if(watched) {
Image(systemName: "checkmark.rectangle.fill").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "xmark.rectangle").foregroundColor(Color.primary).font(.system(size: 20))
}
}
}
}.padding(.leading, 16).padding(.trailing,16)
ScrollView() {
VStack(alignment: .leading) {
if(fullItem.Tagline != "") {
Text(fullItem.Tagline).font(.body).italic().padding(.top, 7).fixedSize(horizontal: false, vertical: true).padding(.leading, 16).padding(.trailing,16)
}
Text(fullItem.Overview).font(.footnote).padding(.top, 3).fixedSize(horizontal: false, vertical: true).padding(.bottom, 3).padding(.leading, 16).padding(.trailing,16)
if(fullItem.Genres.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
HStack() {
Text("Genres:").font(.callout).fontWeight(.semibold)
ForEach(fullItem.Genres, id: \.Id) {genre in
NavigationLink(destination: LibraryView(extraParams: "&Genres=\(genre.Name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")", title: genre.Name)) {
Text(genre.Name).font(.footnote)
}
}
}.padding(.leading, 16).padding(.trailing,16)
}
}
if(fullItem.Cast.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
VStack() {
Spacer().frame(height: 8);
HStack() {
Spacer().frame(width: 16)
ForEach(fullItem.Cast, id: \.Id) { cast in
NavigationLink(destination: LibraryView(extraParams: "&PersonIds=\(cast.Id)", title: cast.Name)) {
VStack() {
WebImage(url: cast.Image)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (cast.ImageBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : cast.ImageBlurHash), size: CGSize(width: 4, height: 4))!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 70, height: 70)
.cornerRadius(10)
}
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(10).shadow(radius: 6)
Text(cast.Name).font(.footnote).fontWeight(.regular).lineLimit(1).frame(width: 100).foregroundColor(Color.primary)
if(cast.Role != "") {
Text(cast.Role).font(.caption).fontWeight(.medium).lineLimit(1).foregroundColor(Color.secondary).frame(width: 100)
}
}
}
Spacer().frame(width: 10)
}
Spacer().frame(width: 16)
}
}
}.padding(.top, -3)
}
if(fullItem.Directors.count != 0) {
HStack() {
Text("Directors:").font(.callout).fontWeight(.semibold)
Text(fullItem.Directors.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,16)
}
if(fullItem.Writers.count != 0) {
HStack() {
Text("Writers:").font(.callout).fontWeight(.semibold)
Text(fullItem.Writers.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,16)
}
if(fullItem.Studios.count != 0) {
HStack() {
Text("Studios:").font(.callout).fontWeight(.semibold)
Text(fullItem.Studios.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,16)
}
Spacer().frame(height: 3)
}
}
}.padding(EdgeInsets(top: UIDevice.current.userInterfaceIdiom == .pad ? 54 : 24, leading: 0, bottom: 0, trailing: 0))
}
}
} else {
GeometryReader { geometry in
ZStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.ParentBackdropItemId)/Images/Backdrop?maxWidth=750&quality=90&tag=\(fullItem.Backdrop)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.BackdropBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.BackdropBlurHash), size: CGSize(width: 32, height: 32))!)
.resizable()
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom)
}
.opacity(0.3)
.aspectRatio(contentMode: .fill)
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom)
.edgesIgnoringSafeArea(.all)
HStack() {
VStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.SeriesId ?? "")/Images/Primary?maxWidth=250&quality=90&tag=\(fullItem.Poster)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 32, height: 32))!)
.resizable()
.frame(width: 120, height: 180)
.cornerRadius(10)
}
.aspectRatio(contentMode: .fill)
.frame(width: 120, height: 180)
.cornerRadius(10)
.shadow(radius: 5)
Spacer().frame(height: 15)
NavigationLink(destination: VideoPlayerViewRefactored()) {
HStack() {
Text(fullItem.Progress == 0 ? "Play" : "\(progressString) left").foregroundColor(Color.white).font(.callout).fontWeight(.semibold)
Image(systemName: "play.fill").foregroundColor(Color.white).font(.system(size: 20))
}
.frame(width: 120, height: 35)
.background(Color(red: 172/255, green: 92/255, blue: 195/255))
.cornerRadius(10)
}
Spacer()
}
ScrollView() {
VStack(alignment: .leading) {
HStack() {
VStack(alignment: .leading) {
Spacer()
Text(fullItem.Name).font(.headline)
.fontWeight(.semibold)
.foregroundColor(.primary)
.fixedSize(horizontal: false, vertical: true)
.offset(y: -4)
.offset(x: 14, y: 0)
Spacer().frame(height: 1)
HStack() {
Text(String(fullItem.ProductionYear)).font(.subheadline)
.fontWeight(.medium)
@ -267,319 +450,115 @@ struct EpisodeItemView: View {
.offset(x: -7, y: 0.7)
}
}
}.frame(maxWidth: .infinity, alignment: .leading)
}.frame(maxWidth: .infinity, alignment: .leading).offset(x: 0, y: UIDevice.current.userInterfaceIdiom == .pad ? -98 : -46).padding(.trailing, 16)
}.offset(x: 16, y: UIDevice.current.userInterfaceIdiom == .pad ? 135 : 40)
, alignment: .bottomLeading)
VStack(alignment: .leading) {
HStack() {
//Play button
Button() {
playing = true;
} label: {
HStack() {
Text(fullItem.Progress == 0 ? "Play" : "\(progressString) left").foregroundColor(Color.white).font(.callout).fontWeight(.semibold)
Image(systemName: "play.fill").foregroundColor(Color.white).font(.system(size: 20))
}
.frame(width: 120, height: 35)
.background(Color(red: 172/255, green: 92/255, blue: 195/255))
.cornerRadius(10)
}.buttonStyle(PlainButtonStyle())
.frame(width: 120, height: 25)
Spacer()
HStack() {
Button() {
favorite.toggle()
} label: {
if(!favorite) {
Image(systemName: "heart").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "heart.fill").foregroundColor(Color(UIColor.systemRed)).font(.system(size: 20))
}
}
Button() {
watched.toggle()
} label: {
if(watched) {
Image(systemName: "checkmark.rectangle.fill").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "xmark.rectangle").foregroundColor(Color.primary).font(.system(size: 20))
}
}
}
}.padding(.leading, 16).padding(.trailing,16)
ScrollView() {
VStack(alignment: .leading) {
if(fullItem.Tagline != "") {
Text(fullItem.Tagline).font(.body).italic().padding(.top, 7).fixedSize(horizontal: false, vertical: true).padding(.leading, 16).padding(.trailing,16)
}
Text(fullItem.Overview).font(.footnote).padding(.top, 3).fixedSize(horizontal: false, vertical: true).padding(.bottom, 3).padding(.leading, 16).padding(.trailing,16)
if(fullItem.Genres.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
HStack() {
Text("Genres:").font(.callout).fontWeight(.semibold)
ForEach(fullItem.Genres, id: \.Id) {genre in
NavigationLink(destination: LibraryView(extraParams: "&Genres=\(genre.Name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")", title: genre.Name)) {
Text(genre.Name).font(.footnote)
}
}
}.padding(.leading, 16).padding(.trailing,16)
}
}
if(fullItem.Cast.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
VStack() {
Spacer().frame(height: 8);
HStack() {
Spacer().frame(width: 16)
ForEach(fullItem.Cast, id: \.Id) { cast in
NavigationLink(destination: LibraryView(extraParams: "&PersonIds=\(cast.Id)", title: cast.Name)) {
VStack() {
WebImage(url: cast.Image)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (cast.ImageBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : cast.ImageBlurHash), size: CGSize(width: 4, height: 4))!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 70, height: 70)
.cornerRadius(10)
}
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(10).shadow(radius: 6)
Text(cast.Name).font(.footnote).fontWeight(.regular).lineLimit(1).frame(width: 100).foregroundColor(Color.primary)
if(cast.Role != "") {
Text(cast.Role).font(.caption).fontWeight(.medium).lineLimit(1).foregroundColor(Color.secondary).frame(width: 100)
}
}
}
Spacer().frame(width: 10)
}
Spacer().frame(width: 16)
}
}
}.padding(.top, -3)
}
if(fullItem.Directors.count != 0) {
HStack() {
Text("Directors:").font(.callout).fontWeight(.semibold)
Text(fullItem.Directors.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,16)
}
if(fullItem.Writers.count != 0) {
HStack() {
Text("Writers:").font(.callout).fontWeight(.semibold)
Text(fullItem.Writers.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,16)
}
if(fullItem.Studios.count != 0) {
HStack() {
Text("Studios:").font(.callout).fontWeight(.semibold)
Text(fullItem.Studios.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,16)
}
Spacer().frame(height: 3)
}
}
}.padding(EdgeInsets(top: UIDevice.current.userInterfaceIdiom == .pad ? 54 : 24, leading: 0, bottom: 0, trailing: 0))
}
}
} else {
GeometryReader { geometry in
ZStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.ParentBackdropItemId)/Images/Backdrop?maxWidth=750&quality=90&tag=\(fullItem.Backdrop)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.BackdropBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.BackdropBlurHash), size: CGSize(width: 32, height: 32))!)
.resizable()
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom)
}
.opacity(0.3)
.aspectRatio(contentMode: .fill)
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom)
.edgesIgnoringSafeArea(.all)
HStack() {
VStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.SeriesId ?? "")/Images/Primary?maxWidth=250&quality=90&tag=\(fullItem.Poster)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 32, height: 32))!)
.resizable()
.frame(width: 120, height: 180)
}
.aspectRatio(contentMode: .fill)
.frame(width: 120, height: 180)
.cornerRadius(10)
.shadow(radius: 5)
Spacer().frame(height: 15)
Button() {
playing = true;
} label: {
HStack() {
Text(fullItem.Progress == 0 ? "Play" : "\(progressString) left").foregroundColor(Color.white).font(.callout).fontWeight(.semibold)
Image(systemName: "play.fill").foregroundColor(Color.white).font(.system(size: 20))
}
.frame(width: 120, height: 35)
.background(Color(red: 172/255, green: 92/255, blue: 195/255))
.cornerRadius(10)
}.buttonStyle(PlainButtonStyle())
.frame(width: 120, height: 25)
Spacer()
}
ScrollView() {
VStack(alignment: .leading) {
HStack() {
VStack(alignment: .leading) {
Text(fullItem.Name).font(.headline)
.fontWeight(.semibold)
.foregroundColor(.primary)
.fixedSize(horizontal: false, vertical: true)
.offset(x: 14, y: 0)
Spacer().frame(height: 1)
HStack() {
Text(String(fullItem.ProductionYear)).font(.subheadline)
.fontWeight(.medium)
.foregroundColor(.secondary)
.lineLimit(1)
Text(fullItem.Runtime).font(.subheadline)
.fontWeight(.medium)
.foregroundColor(.secondary)
.lineLimit(1)
if(fullItem.OfficialRating != "") {
Text(fullItem.OfficialRating).font(.subheadline)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.lineLimit(1)
.padding(EdgeInsets(top: 1, leading: 4, bottom: 1, trailing: 4))
.overlay(
RoundedRectangle(cornerRadius: 2)
.stroke(Color.secondary, lineWidth: 1)
)
}
if(fullItem.CommunityRating != "0") {
HStack() {
Image(systemName: "star").foregroundColor(.secondary)
Text(fullItem.CommunityRating).font(.subheadline)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.lineLimit(1)
.offset(x: -7, y: 0.7)
}
}
Spacer()
}.frame(maxWidth: .infinity)
.offset(x: 14)
Spacer()
}.frame(maxWidth: .infinity)
Spacer()
.offset(x: 14)
}.frame(maxWidth: .infinity)
Spacer()
HStack() {
Button() {
favorite.toggle()
} label: {
if(!favorite) {
Image(systemName: "heart").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "heart.fill").foregroundColor(Color(UIColor.systemRed)).font(.system(size: 20))
}
}
Button() {
watched.toggle()
} label: {
if(watched) {
Image(systemName: "checkmark.rectangle.fill").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "xmark.rectangle").foregroundColor(Color.primary).font(.system(size: 20))
}
}
}
}.padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
if(fullItem.Tagline != "") {
Text(fullItem.Tagline).font(.body).italic().padding(.top, 3).fixedSize(horizontal: false, vertical: true).padding(.leading, 16).padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
Text(fullItem.Overview).font(.footnote).padding(.top, 3).fixedSize(horizontal: false, vertical: true).padding(.bottom, 3).padding(.leading, 16).padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
if(fullItem.Genres.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
HStack() {
Button() {
favorite.toggle()
} label: {
if(!favorite) {
Image(systemName: "heart").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "heart.fill").foregroundColor(Color(UIColor.systemRed)).font(.system(size: 20))
Text("Genres:").font(.callout).fontWeight(.semibold)
ForEach(fullItem.Genres, id: \.Id) {genre in
NavigationLink(destination: LibraryView(extraParams: "&Genres=\(genre.Name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")", title: genre.Name)) {
Text(genre.Name).font(.footnote)
}
}
Button() {
watched.toggle()
} label: {
if(watched) {
Image(systemName: "checkmark.rectangle.fill").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "xmark.rectangle").foregroundColor(Color.primary).font(.system(size: 20))
}
}
}
}.padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
if(fullItem.Tagline != "") {
Text(fullItem.Tagline).font(.body).italic().padding(.top, 3).fixedSize(horizontal: false, vertical: true).padding(.leading, 16).padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}.padding(.leading, 16).padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
Text(fullItem.Overview).font(.footnote).padding(.top, 3).fixedSize(horizontal: false, vertical: true).padding(.bottom, 3).padding(.leading, 16).padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
if(fullItem.Genres.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
}
if(fullItem.Cast.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
VStack() {
Spacer().frame(height: 8);
HStack() {
Text("Genres:").font(.callout).fontWeight(.semibold)
ForEach(fullItem.Genres, id: \.Id) {genre in
NavigationLink(destination: LibraryView(extraParams: "&Genres=\(genre.Name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")", title: genre.Name)) {
Text(genre.Name).font(.footnote)
}
}
}.padding(.leading, 16).padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
}
if(fullItem.Cast.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
VStack() {
Spacer().frame(height: 8);
HStack() {
Spacer().frame(width: 16)
ForEach(fullItem.Cast, id: \.Id) { cast in
NavigationLink(destination: LibraryView(extraParams: "&PersonIds=\(cast.Id)", title: cast.Name)) {
VStack() {
WebImage(url: cast.Image)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (cast.ImageBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : cast.ImageBlurHash), size: CGSize(width: 32, height: 32))!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(10)
}
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(10).shadow(radius: 6)
Text(cast.Name).font(.footnote).fontWeight(.regular).lineLimit(1).frame(width: 100).foregroundColor(Color.primary)
if(cast.Role != "") {
Text(cast.Role).font(.caption).fontWeight(.medium).lineLimit(1).foregroundColor(Color.secondary).frame(width: 100)
Spacer().frame(width: 16)
ForEach(fullItem.Cast, id: \.Id) { cast in
NavigationLink(destination: LibraryView(extraParams: "&PersonIds=\(cast.Id)", title: cast.Name)) {
VStack() {
WebImage(url: cast.Image)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (cast.ImageBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : cast.ImageBlurHash), size: CGSize(width: 32, height: 32))!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(10)
}
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(10).shadow(radius: 6)
Text(cast.Name).font(.footnote).fontWeight(.regular).lineLimit(1).frame(width: 100).foregroundColor(Color.primary)
if(cast.Role != "") {
Text(cast.Role).font(.caption).fontWeight(.medium).lineLimit(1).foregroundColor(Color.secondary).frame(width: 100)
}
}
Spacer().frame(width: 10)
}
Spacer().frame(width: UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
Spacer().frame(width: 10)
}
Spacer().frame(width: UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
}.padding(.top, -3)
}
if(fullItem.Directors.count != 0) {
HStack() {
Text("Directors:").font(.callout).fontWeight(.semibold)
Text(fullItem.Directors.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
if(fullItem.Writers.count != 0) {
HStack() {
Text("Writers:").font(.callout).fontWeight(.semibold)
Text(fullItem.Writers.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
if(fullItem.Studios.count != 0) {
HStack() {
Text("Studios:").font(.callout).fontWeight(.semibold)
Text(fullItem.Studios.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
Spacer().frame(height: 100);
}.frame(maxHeight: .infinity)
}
}.padding(.top, 16).padding(.leading, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55).edgesIgnoringSafeArea(.leading)
}
}
}.padding(.top, -3)
}
if(fullItem.Directors.count != 0) {
HStack() {
Text("Directors:").font(.callout).fontWeight(.semibold)
Text(fullItem.Directors.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
if(fullItem.Writers.count != 0) {
HStack() {
Text("Writers:").font(.callout).fontWeight(.semibold)
Text(fullItem.Writers.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
if(fullItem.Studios.count != 0) {
HStack() {
Text("Studios:").font(.callout).fontWeight(.semibold)
Text(fullItem.Studios.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
Spacer().frame(height: 100);
}.frame(maxHeight: .infinity)
}
}.padding(.top, 16).padding(.leading, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55).edgesIgnoringSafeArea(.leading)
}
}
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("\(fullItem.Name) - S\(String(fullItem.ParentIndexNumber ?? 0)):E\(String(fullItem.IndexNumber ?? 0)) - \(fullItem.SeriesName ?? "")")
.introspectTabBarController { (UITabBarController) in
UITabBarController.tabBar.isHidden = false
}
}.onAppear(perform: loadData)
.supportedOrientations(.allButUpsideDown)
.overrideViewPreference(.unspecified)
.preferredColorScheme(.none)
.prefersHomeIndicatorAutoHidden(false)
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("\(fullItem.Name) - S\(String(fullItem.ParentIndexNumber ?? 0)):E\(String(fullItem.IndexNumber ?? 0)) - \(fullItem.SeriesName ?? "")")
}.onAppear(perform: loadData)
.supportedOrientations(.allButUpsideDown)
.overrideViewPreference(.unspecified)
.preferredColorScheme(.none)
.prefersHomeIndicatorAutoHidden(false)
}
}

View File

@ -6,14 +6,8 @@
//
import SwiftUI
import SwiftyRequest
import SwiftyJSON
import Introspect
import SDWebImageSwiftUI
struct ItemView: View {
@EnvironmentObject var globalData: GlobalData
@State private var isLoading: Bool = false;
var item: ResumeItem;
init(item: ResumeItem) {

View File

@ -11,6 +11,14 @@ class justSignedIn: ObservableObject {
@Published var did: Bool = false
}
class GlobalData: ObservableObject {
@Published var user: SignedInUser?
@Published var authToken: String = ""
@Published var server: Server?
@Published var authHeader: String = ""
@Published var isInNetwork: Bool = true;
}
extension UIDevice {
var hasNotch: Bool {
let bottom = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
@ -49,6 +57,130 @@ class OrientationInfo: ObservableObject {
}
}
extension View {
func withHostingWindow(_ callback: @escaping (UIWindow?) -> Void) -> some View {
self.background(HostingWindowFinder(callback: callback))
}
}
struct HostingWindowFinder: UIViewRepresentable {
var callback: (UIWindow?) -> ()
func makeUIView(context: Context) -> UIView {
let view = UIView()
DispatchQueue.main.async { [weak view] in
callback(view?.window)
}
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
}
}
struct PrefersHomeIndicatorAutoHiddenPreferenceKey: PreferenceKey {
typealias Value = Bool
static var defaultValue: Value = false
static func reduce(value: inout Value, nextValue: () -> Value) {
value = nextValue() || value
}
}
struct ViewPreferenceKey: PreferenceKey {
typealias Value = UIUserInterfaceStyle
static var defaultValue: UIUserInterfaceStyle = .unspecified
static func reduce(value: inout UIUserInterfaceStyle, nextValue: () -> UIUserInterfaceStyle) {
value = nextValue()
}
}
struct SupportedOrientationsPreferenceKey: PreferenceKey {
typealias Value = UIInterfaceOrientationMask
static var defaultValue: UIInterfaceOrientationMask = .allButUpsideDown
static func reduce(value: inout UIInterfaceOrientationMask, nextValue: () -> UIInterfaceOrientationMask) {
// use the most restrictive set from the stack
value.formIntersection(nextValue())
}
}
class PreferenceUIHostingController: UIHostingController<AnyView> {
init<V: View>(wrappedView: V) {
let box = Box()
super.init(rootView: AnyView(wrappedView
.onPreferenceChange(PrefersHomeIndicatorAutoHiddenPreferenceKey.self) {
box.value?._prefersHomeIndicatorAutoHidden = $0
}.onPreferenceChange(SupportedOrientationsPreferenceKey.self) {
box.value?._orientations = $0
}.onPreferenceChange(ViewPreferenceKey.self) {
box.value?._viewPreference = $0
}
))
box.value = self
}
@objc required dynamic init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
super.modalPresentationStyle = .fullScreen
}
private class Box {
weak var value: PreferenceUIHostingController?
init() {}
}
// MARK: Prefers Home Indicator Auto Hidden
public var _prefersHomeIndicatorAutoHidden = false {
didSet { setNeedsUpdateOfHomeIndicatorAutoHidden() }
}
override var prefersHomeIndicatorAutoHidden: Bool {
_prefersHomeIndicatorAutoHidden
}
// MARK: Lock orientation
public var _orientations: UIInterfaceOrientationMask = .allButUpsideDown {
didSet {
UIViewController.attemptRotationToDeviceOrientation();
if(_orientations == .landscape) {
let value = UIInterfaceOrientation.landscapeRight.rawValue;
UIDevice.current.setValue(value, forKey: "orientation")
}
}
};
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
_orientations
}
public var _viewPreference: UIUserInterfaceStyle = .unspecified {
didSet {
overrideUserInterfaceStyle = _viewPreference
}
};
}
extension View {
// Controls the application's preferred home indicator auto-hiding when this view is shown.
func prefersHomeIndicatorAutoHidden(_ value: Bool) -> some View {
preference(key: PrefersHomeIndicatorAutoHiddenPreferenceKey.self, value: value)
}
func supportedOrientations(_ supportedOrientations: UIInterfaceOrientationMask) -> some View {
// When rendered, export the requested orientations upward to Root
preference(key: SupportedOrientationsPreferenceKey.self, value: supportedOrientations)
}
func overrideViewPreference(_ viewPreference: UIUserInterfaceStyle) -> some View {
// When rendered, export the requested orientations upward to Root
preference(key: ViewPreferenceKey.self, value: viewPreference)
}
}
@main
struct JellyfinPlayerApp: App {
let persistenceController = PersistenceController.shared

View File

@ -119,7 +119,7 @@ struct LatestMediaView: View {
.cornerRadius(10.0)
.padding(3), alignment: .topTrailing
).shadow(radius: 6)
)
} else {
Spacer().frame(height:10)
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=250&quality=80&tag=\(item.Image)")!)
@ -132,7 +132,6 @@ struct LatestMediaView: View {
}
.frame(width: 100, height: 150)
.cornerRadius(10)
.shadow(radius: 6)
}
Text(item.Name)
.font(.caption)

View File

@ -218,7 +218,6 @@ struct LibraryView: View {
}
.frame(width:100, height: 150)
.cornerRadius(10)
.shadow(radius: 5)
} else {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=250&quality=80&tag=\(item.Image)"))
.resizable()
@ -246,7 +245,7 @@ struct LibraryView: View {
.opacity(0.8)
.cornerRadius(10.0)
.padding(3), alignment: .topTrailing
).shadow(radius: 5)
)
}
Text(item.Name)
.font(.caption)

View File

@ -8,7 +8,6 @@
import SwiftUI
import SwiftyRequest
import SwiftyJSON
import Introspect
import SDWebImageSwiftUI
class DetailItem: ObservableObject {
@ -182,7 +181,7 @@ struct MovieItemView: View {
let imageTag = person["PrimaryImageTag"].string ?? "";
cast.ImageBlurHash = person["ImageBlurHashes"]["Primary"][imageTag].string ?? "";
cast.Role = person["Role"].string ?? "";
cast.Image = URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(cast.Id)/Images/Primary?maxWidth=150&quality=85&tag=\(imageTag)")!
cast.Image = URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(cast.Id)/Images/Primary?maxWidth=250&quality=85&tag=\(imageTag)")!
fullItem.Cast.append(cast);
}
}
@ -237,53 +236,236 @@ struct MovieItemView: View {
}
var body: some View {
if(playing) {
VideoPlayerView(item: fullItem, playing: $playing)
.supportedOrientations(.landscape)
.preferredColorScheme(.dark)
.overrideViewPreference(.dark)
.prefersHomeIndicatorAutoHidden(true)
.introspectTabBarController { (UITabBarController) in
UITabBarController.tabBar.isHidden = true
}
} else {
LoadingView(isShowing: $isLoading) {
VStack(alignment:.leading) {
if(!isLoading) {
if(orientationInfo.orientation == .portrait) {
GeometryReader { geometry in
VStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Backdrop?maxWidth=450&quality=90&tag=\(fullItem.Backdrop)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.BackdropBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.BackdropBlurHash), size: CGSize(width: 32, height: 32))!)
.resizable()
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: UIDevice.current.userInterfaceIdiom == .pad ? 350 : (geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing) * 0.5625)
}
.opacity(0.3)
.aspectRatio(contentMode: .fill)
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: UIDevice.current.userInterfaceIdiom == .pad ? 350 : (geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing) * 0.5625)
.shadow(radius: 5)
.overlay(
LoadingView(isShowing: $isLoading) {
VStack(alignment:.leading) {
if(!isLoading) {
if(orientationInfo.orientation == .portrait) {
GeometryReader { geometry in
VStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Backdrop?maxWidth=450&quality=90&tag=\(fullItem.Backdrop)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.BackdropBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.BackdropBlurHash), size: CGSize(width: 32, height: 32))!)
.resizable()
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: UIDevice.current.userInterfaceIdiom == .pad ? 350 : (geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing) * 0.5625)
}
.opacity(0.3)
.aspectRatio(contentMode: .fill)
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: UIDevice.current.userInterfaceIdiom == .pad ? 350 : (geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing) * 0.5625)
.shadow(radius: 5)
.overlay(
HStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Primary?maxWidth=250&quality=90&tag=\(fullItem.Poster)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 32, height: 32))!)
.resizable()
.frame(width: 120, height: 180)
.cornerRadius(10)
}.aspectRatio(contentMode: .fill)
.frame(width: 120, height: 180)
.cornerRadius(10)
VStack(alignment: .leading) {
Spacer()
Text(fullItem.Name).font(.headline)
.fontWeight(.semibold)
.foregroundColor(.primary)
.fixedSize(horizontal: false, vertical: true)
.offset(y: -4)
HStack() {
Text(String(fullItem.ProductionYear)).font(.subheadline)
.fontWeight(.medium)
.foregroundColor(.secondary)
.lineLimit(1)
Text(fullItem.Runtime).font(.subheadline)
.fontWeight(.medium)
.foregroundColor(.secondary)
.lineLimit(1)
if(fullItem.OfficialRating != "") {
Text(fullItem.OfficialRating).font(.subheadline)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.lineLimit(1)
.padding(EdgeInsets(top: 1, leading: 4, bottom: 1, trailing: 4))
.overlay(
RoundedRectangle(cornerRadius: 2)
.stroke(Color.secondary, lineWidth: 1)
)
}
if(fullItem.CommunityRating != "0") {
HStack() {
Image(systemName: "star").foregroundColor(.secondary)
Text(fullItem.CommunityRating).font(.subheadline)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.lineLimit(1)
.offset(x: -7, y: 0.7)
}
}
}.frame(maxWidth: .infinity, alignment: .leading)
}.offset(x: 0, y: UIDevice.current.userInterfaceIdiom == .pad ? -98 : -46).padding(.trailing, 16)
}.offset(x: 16, y: UIDevice.current.userInterfaceIdiom == .pad ? 135 : 40)
, alignment: .bottomLeading)
VStack(alignment: .leading) {
HStack() {
//Play button
NavigationLink(destination: VideoPlayerViewRefactored()) {
HStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Primary?maxWidth=250&quality=90&tag=\(fullItem.Poster)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 32, height: 32))!)
.resizable()
.frame(width: 120, height: 180)
.cornerRadius(10)
}.aspectRatio(contentMode: .fill)
Text(fullItem.Progress == 0 ? "Play" : "\(progressString) left").foregroundColor(Color.white).font(.callout).fontWeight(.semibold)
Image(systemName: "play.fill").foregroundColor(Color.white).font(.system(size: 20))
}
.frame(width: 120, height: 35)
.background(Color(red: 172/255, green: 92/255, blue: 195/255))
.cornerRadius(10)
}
Spacer()
HStack() {
Button() {
favorite.toggle()
} label: {
if(!favorite) {
Image(systemName: "heart").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "heart.fill").foregroundColor(Color(UIColor.systemRed)).font(.system(size: 20))
}
}
Button() {
watched.toggle()
} label: {
if(watched) {
Image(systemName: "checkmark.rectangle.fill").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "xmark.rectangle").foregroundColor(Color.primary).font(.system(size: 20))
}
}
}
}.padding(.leading, 16).padding(.trailing,16)
ScrollView() {
VStack(alignment: .leading) {
if(fullItem.Tagline != "") {
Text(fullItem.Tagline).font(.body).italic().padding(.top, 7).fixedSize(horizontal: false, vertical: true).padding(.leading, 16).padding(.trailing,16)
}
Text(fullItem.Overview).font(.footnote).padding(.top, 3).fixedSize(horizontal: false, vertical: true).padding(.bottom, 3).padding(.leading, 16).padding(.trailing,16)
if(fullItem.Genres.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
HStack() {
Text("Genres:").font(.callout).fontWeight(.semibold)
ForEach(fullItem.Genres, id: \.Id) {genre in
NavigationLink(destination: LibraryView(extraParams: "&Genres=\(genre.Name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")", title: genre.Name)) {
Text(genre.Name).font(.footnote)
}
}
}.padding(.leading, 16).padding(.trailing,16)
}
}
if(fullItem.Cast.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
VStack() {
Spacer().frame(height: 8);
HStack() {
Spacer().frame(width: 16)
ForEach(fullItem.Cast, id: \.Id) { cast in
NavigationLink(destination: LibraryView(extraParams: "&PersonIds=\(cast.Id)", title: cast.Name)) {
VStack() {
WebImage(url: cast.Image)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (cast.ImageBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : cast.ImageBlurHash), size: CGSize(width: 16, height: 16))!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(10)
}
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(10).shadow(radius: 6)
Text(cast.Name).font(.footnote).fontWeight(.regular).lineLimit(1).frame(width: 100).foregroundColor(Color.primary)
if(cast.Role != "") {
Text(cast.Role).font(.caption).fontWeight(.medium).lineLimit(1).foregroundColor(Color.secondary).frame(width: 100)
}
}
}
Spacer().frame(width: 10)
}
Spacer().frame(width: 16)
}
}
}.padding(.top, -3)
}
if(fullItem.Directors.count != 0) {
HStack() {
Text("Directors:").font(.callout).fontWeight(.semibold)
Text(fullItem.Directors.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,16)
}
if(fullItem.Writers.count != 0) {
HStack() {
Text("Writers:").font(.callout).fontWeight(.semibold)
Text(fullItem.Writers.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,16)
}
if(fullItem.Studios.count != 0) {
HStack() {
Text("Studios:").font(.callout).fontWeight(.semibold)
Text(fullItem.Studios.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,16)
}
Spacer().frame(height: 3)
}
}
}.padding(EdgeInsets(top: UIDevice.current.userInterfaceIdiom == .pad ? 54 : 24, leading: 0, bottom: 0, trailing: 0))
}
}
} else {
GeometryReader { geometry in
ZStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Backdrop?maxWidth=750&quality=90&tag=\(fullItem.Backdrop)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.BackdropBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.BackdropBlurHash), size: CGSize(width: 16, height: 16))!)
.resizable()
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom)
}
.opacity(0.3)
.aspectRatio(contentMode: .fill)
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom)
.edgesIgnoringSafeArea(.all)
HStack() {
VStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Primary?maxWidth=250&quality=90&tag=\(fullItem.Poster)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 16, height: 16))!)
.resizable()
.frame(width: 120, height: 180)
.cornerRadius(10)
}
.frame(width: 120, height: 180)
.cornerRadius(10)
.shadow(radius: 5)
Spacer().frame(height: 15)
NavigationLink(destination: VideoPlayerViewRefactored()) {
HStack() {
Text(fullItem.Progress == 0 ? "Play" : "\(progressString) left").foregroundColor(Color.white).font(.callout).fontWeight(.semibold)
Image(systemName: "play.fill").foregroundColor(Color.white).font(.system(size: 20))
}
.frame(width: 120, height: 35)
.background(Color(red: 172/255, green: 92/255, blue: 195/255))
.cornerRadius(10)
}
Spacer()
}
ScrollView() {
VStack(alignment: .leading) {
HStack() {
VStack(alignment: .leading) {
Spacer()
Text(fullItem.Name).font(.headline)
.fontWeight(.semibold)
.foregroundColor(.primary)
.fixedSize(horizontal: false, vertical: true)
.offset(y: -4)
.offset(x: 14, y: 0)
Spacer().frame(height: 1)
HStack() {
Text(String(fullItem.ProductionYear)).font(.subheadline)
.fontWeight(.medium)
@ -314,318 +496,115 @@ struct MovieItemView: View {
.offset(x: -7, y: 0.7)
}
}
Spacer()
}.frame(maxWidth: .infinity, alignment: .leading)
}.offset(x: 0, y: UIDevice.current.userInterfaceIdiom == .pad ? -98 : -46).padding(.trailing, 16)
}.offset(x: 16, y: UIDevice.current.userInterfaceIdiom == .pad ? 135 : 40)
, alignment: .bottomLeading)
VStack(alignment: .leading) {
HStack() {
//Play button
Button() {
playing = true;
} label: {
.offset(x: 14)
}.frame(maxWidth: .infinity, alignment: .leading)
Spacer()
HStack() {
Text(fullItem.Progress == 0 ? "Play" : "\(progressString) left").foregroundColor(Color.white).font(.callout).fontWeight(.semibold)
Image(systemName: "play.fill").foregroundColor(Color.white).font(.system(size: 20))
}
.frame(width: 120, height: 35)
.background(Color(red: 172/255, green: 92/255, blue: 195/255))
.cornerRadius(10)
}.buttonStyle(PlainButtonStyle())
.frame(width: 120, height: 25)
Spacer()
HStack() {
Button() {
favorite.toggle()
} label: {
if(!favorite) {
Image(systemName: "heart").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "heart.fill").foregroundColor(Color(UIColor.systemRed)).font(.system(size: 20))
Button() {
favorite.toggle()
} label: {
if(!favorite) {
Image(systemName: "heart").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "heart.fill").foregroundColor(Color(UIColor.systemRed)).font(.system(size: 20))
}
}
Button() {
watched.toggle()
} label: {
if(watched) {
Image(systemName: "checkmark.rectangle.fill").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "xmark.rectangle").foregroundColor(Color.primary).font(.system(size: 20))
}
}
}
Button() {
watched.toggle()
} label: {
if(watched) {
Image(systemName: "checkmark.rectangle.fill").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "xmark.rectangle").foregroundColor(Color.primary).font(.system(size: 20))
}
}.padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
if(fullItem.Tagline != "") {
Text(fullItem.Tagline).font(.body).italic().padding(.top, 3).fixedSize(horizontal: false, vertical: true).padding(.leading, 16).padding(.trailing,UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
Text(fullItem.Overview).font(.footnote).padding(.top, 3).fixedSize(horizontal: false, vertical: true).padding(.bottom, 3).padding(.leading, 16).padding(.trailing,UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
if(fullItem.Genres.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
HStack() {
Text("Genres:").font(.callout).fontWeight(.semibold)
ForEach(fullItem.Genres, id: \.Id) {genre in
NavigationLink(destination: LibraryView(extraParams: "&Genres=\(genre.Name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")", title: genre.Name)) {
Text(genre.Name).font(.footnote)
}
}
}.padding(.leading, 16).padding(.trailing,UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
}
}.padding(.leading, 16).padding(.trailing,16)
ScrollView() {
VStack(alignment: .leading) {
if(fullItem.Tagline != "") {
Text(fullItem.Tagline).font(.body).italic().padding(.top, 7).fixedSize(horizontal: false, vertical: true).padding(.leading, 16).padding(.trailing,16)
}
Text(fullItem.Overview).font(.footnote).padding(.top, 3).fixedSize(horizontal: false, vertical: true).padding(.bottom, 3).padding(.leading, 16).padding(.trailing,16)
if(fullItem.Genres.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
if(fullItem.Cast.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
VStack() {
Spacer().frame(height: 8);
HStack() {
Text("Genres:").font(.callout).fontWeight(.semibold)
ForEach(fullItem.Genres, id: \.Id) {genre in
NavigationLink(destination: LibraryView(extraParams: "&Genres=\(genre.Name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")", title: genre.Name)) {
Text(genre.Name).font(.footnote)
}
}
}.padding(.leading, 16).padding(.trailing,16)
}
}
if(fullItem.Cast.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
VStack() {
Spacer().frame(height: 8);
HStack() {
Spacer().frame(width: 16)
ForEach(fullItem.Cast, id: \.Id) { cast in
NavigationLink(destination: LibraryView(extraParams: "&PersonIds=\(cast.Id)", title: cast.Name)) {
VStack() {
WebImage(url: cast.Image)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (cast.ImageBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : cast.ImageBlurHash), size: CGSize(width: 16, height: 16))!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(10)
}
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(10).shadow(radius: 6)
Text(cast.Name).font(.footnote).fontWeight(.regular).lineLimit(1).frame(width: 100).foregroundColor(Color.primary)
if(cast.Role != "") {
Text(cast.Role).font(.caption).fontWeight(.medium).lineLimit(1).foregroundColor(Color.secondary).frame(width: 100)
Spacer().frame(width: 16)
ForEach(fullItem.Cast, id: \.Id) { cast in
NavigationLink(destination: LibraryView(extraParams: "&PersonIds=\(cast.Id)", title: cast.Name)) {
VStack() {
WebImage(url: cast.Image)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (cast.ImageBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : cast.ImageBlurHash), size: CGSize(width: 16, height: 16))!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(10)
}
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(10).shadow(radius: 6)
Text(cast.Name).font(.footnote).fontWeight(.regular).lineLimit(1).frame(width: 100).foregroundColor(Color.primary)
if(cast.Role != "") {
Text(cast.Role).font(.caption).fontWeight(.medium).lineLimit(1).foregroundColor(Color.secondary).frame(width: 100)
}
}
Spacer().frame(width: 10)
}
Spacer().frame(width: 16)
Spacer().frame(width: 10)
}
Spacer().frame(width: UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
}.padding(.top, -3)
}
if(fullItem.Directors.count != 0) {
HStack() {
Text("Directors:").font(.callout).fontWeight(.semibold)
Text(fullItem.Directors.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,16)
}
if(fullItem.Writers.count != 0) {
HStack() {
Text("Writers:").font(.callout).fontWeight(.semibold)
Text(fullItem.Writers.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,16)
}
if(fullItem.Studios.count != 0) {
HStack() {
Text("Studios:").font(.callout).fontWeight(.semibold)
Text(fullItem.Studios.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,16)
}
Spacer().frame(height: 3)
}
}.padding(.top, -3).padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? -55 : 0)
}
}
}.padding(EdgeInsets(top: UIDevice.current.userInterfaceIdiom == .pad ? 54 : 24, leading: 0, bottom: 0, trailing: 0))
}
}
} else {
GeometryReader { geometry in
ZStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Backdrop?maxWidth=750&quality=90&tag=\(fullItem.Backdrop)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.BackdropBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.BackdropBlurHash), size: CGSize(width: 16, height: 16))!)
.resizable()
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom)
}
.opacity(0.3)
.aspectRatio(contentMode: .fill)
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom)
.edgesIgnoringSafeArea(.all)
HStack() {
VStack() {
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Primary?maxWidth=250&quality=90&tag=\(fullItem.Poster)")!)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 16, height: 16))!)
.resizable()
.frame(width: 120, height: 180)
}
.frame(width: 120, height: 180)
.cornerRadius(10)
.shadow(radius: 5)
Spacer().frame(height: 15)
Button() {
playing = true;
} label: {
if(fullItem.Directors.count != 0) {
HStack() {
Text(fullItem.Progress == 0 ? "Play" : "\(progressString) left").foregroundColor(Color.white).font(.callout).fontWeight(.semibold)
Image(systemName: "play.fill").foregroundColor(Color.white).font(.system(size: 20))
}
.frame(width: 120, height: 35)
.background(Color(red: 172/255, green: 92/255, blue: 195/255))
.cornerRadius(10)
}.buttonStyle(PlainButtonStyle())
.frame(width: 120, height: 25)
Spacer()
}
ScrollView() {
VStack(alignment: .leading) {
Text("Directors:").font(.callout).fontWeight(.semibold)
Text(fullItem.Directors.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
if(fullItem.Writers.count != 0) {
HStack() {
VStack(alignment: .leading) {
Text(fullItem.Name).font(.headline)
.fontWeight(.semibold)
.foregroundColor(.primary)
.fixedSize(horizontal: false, vertical: true)
.offset(x: 14, y: 0)
Spacer().frame(height: 1)
HStack() {
Text(String(fullItem.ProductionYear)).font(.subheadline)
.fontWeight(.medium)
.foregroundColor(.secondary)
.lineLimit(1)
Text(fullItem.Runtime).font(.subheadline)
.fontWeight(.medium)
.foregroundColor(.secondary)
.lineLimit(1)
if(fullItem.OfficialRating != "") {
Text(fullItem.OfficialRating).font(.subheadline)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.lineLimit(1)
.padding(EdgeInsets(top: 1, leading: 4, bottom: 1, trailing: 4))
.overlay(
RoundedRectangle(cornerRadius: 2)
.stroke(Color.secondary, lineWidth: 1)
)
}
if(fullItem.CommunityRating != "0") {
HStack() {
Image(systemName: "star").foregroundColor(.secondary)
Text(fullItem.CommunityRating).font(.subheadline)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.lineLimit(1)
.offset(x: -7, y: 0.7)
}
}
Spacer()
}.frame(maxWidth: .infinity, alignment: .leading)
.offset(x: 14)
}.frame(maxWidth: .infinity, alignment: .leading)
Spacer()
HStack() {
Button() {
favorite.toggle()
} label: {
if(!favorite) {
Image(systemName: "heart").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "heart.fill").foregroundColor(Color(UIColor.systemRed)).font(.system(size: 20))
}
}
Button() {
watched.toggle()
} label: {
if(watched) {
Image(systemName: "checkmark.rectangle.fill").foregroundColor(Color.primary).font(.system(size: 20))
} else {
Image(systemName: "xmark.rectangle").foregroundColor(Color.primary).font(.system(size: 20))
}
}
}
}.padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
if(fullItem.Tagline != "") {
Text(fullItem.Tagline).font(.body).italic().padding(.top, 3).fixedSize(horizontal: false, vertical: true).padding(.leading, 16).padding(.trailing,UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
Text(fullItem.Overview).font(.footnote).padding(.top, 3).fixedSize(horizontal: false, vertical: true).padding(.bottom, 3).padding(.leading, 16).padding(.trailing,UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
if(fullItem.Genres.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
HStack() {
Text("Genres:").font(.callout).fontWeight(.semibold)
ForEach(fullItem.Genres, id: \.Id) {genre in
NavigationLink(destination: LibraryView(extraParams: "&Genres=\(genre.Name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")", title: genre.Name)) {
Text(genre.Name).font(.footnote)
}
}
}.padding(.leading, 16).padding(.trailing,UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
}
if(fullItem.Cast.count != 0) {
ScrollView(.horizontal, showsIndicators: false) {
VStack() {
Spacer().frame(height: 8);
HStack() {
Spacer().frame(width: 16)
ForEach(fullItem.Cast, id: \.Id) { cast in
NavigationLink(destination: LibraryView(extraParams: "&PersonIds=\(cast.Id)", title: cast.Name)) {
VStack() {
WebImage(url: cast.Image)
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder {
Image(uiImage: UIImage(blurHash: (cast.ImageBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : cast.ImageBlurHash), size: CGSize(width: 16, height: 16))!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(10)
}
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(10).shadow(radius: 6)
Text(cast.Name).font(.footnote).fontWeight(.regular).lineLimit(1).frame(width: 100).foregroundColor(Color.primary)
if(cast.Role != "") {
Text(cast.Role).font(.caption).fontWeight(.medium).lineLimit(1).foregroundColor(Color.secondary).frame(width: 100)
}
}
}
Spacer().frame(width: 10)
}
Spacer().frame(width: UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
}
}.padding(.top, -3).padding(.trailing, UIDevice.current.userInterfaceIdiom == .pad ? -55 : 0)
}
if(fullItem.Directors.count != 0) {
HStack() {
Text("Directors:").font(.callout).fontWeight(.semibold)
Text(fullItem.Directors.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
if(fullItem.Writers.count != 0) {
HStack() {
Text("Writers:").font(.callout).fontWeight(.semibold)
Text(fullItem.Writers.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
if(fullItem.Studios.count != 0) {
HStack() {
Text("Studios:").font(.callout).fontWeight(.semibold)
Text(fullItem.Studios.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
Spacer().frame(height: 195);
}.frame(maxHeight: .infinity)
}
}.padding(.top, 16).padding(.leading, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55).edgesIgnoringSafeArea(.leading)
}
Text("Writers:").font(.callout).fontWeight(.semibold)
Text(fullItem.Writers.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
if(fullItem.Studios.count != 0) {
HStack() {
Text("Studios:").font(.callout).fontWeight(.semibold)
Text(fullItem.Studios.joined(separator: ", ")).font(.footnote).lineLimit(1).foregroundColor(Color.secondary)
}.padding(.leading, 16).padding(.trailing,UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
Spacer().frame(height: 195);
}.frame(maxHeight: .infinity)
}
}.padding(.top, 16).padding(.leading, UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55).edgesIgnoringSafeArea(.leading)
}
}
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(fullItem.Name)
.introspectTabBarController { (UITabBarController) in
UITabBarController.tabBar.isHidden = false
}
}.onAppear(perform: loadData)
.supportedOrientations(.allButUpsideDown)
.overrideViewPreference(.unspecified)
.preferredColorScheme(.none)
.prefersHomeIndicatorAutoHidden(false)
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(fullItem.Name)
}.onAppear(perform: loadData)
.supportedOrientations(.allButUpsideDown)
.overrideViewPreference(.unspecified)
.preferredColorScheme(.none)
.prefersHomeIndicatorAutoHidden(false)
}
}

View File

@ -89,7 +89,6 @@ struct NextUpView: View {
}
.frame(width: 100, height: 150)
.cornerRadius(10)
.shadow(radius: 6)
Text(item.SeriesName ?? "")
.font(.caption)
.fontWeight(.semibold)

View File

@ -8,7 +8,6 @@
import SwiftUI
import SwiftyRequest
import SwiftyJSON
import Introspect
import SDWebImageSwiftUI
struct SeasonItemView: View {

View File

@ -392,9 +392,6 @@ struct VideoPlayerView: View {
vlcplayer.stop()
}).padding(EdgeInsets(top: 0, leading: UIDevice.current.hasNotch ? 30 : 0, bottom: 0, trailing: UIDevice.current.hasNotch ? 30 : 0))
}
.introspectTabBarController { (UITabBarController) in
UITabBarController.tabBar.isHidden = true
}
.overlay(
VStack() {
HStack() {
@ -489,7 +486,6 @@ struct VideoPlayerView: View {
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(.black).opacity(0.4))
.isHidden(inactivity)
, alignment: .topLeading)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.onAppear(perform: startStream)

View File

@ -0,0 +1,27 @@
//
// VideoPlayerViewRefactored.swift
// JellyfinPlayer
//
// Created by Aiden Vigue on 5/26/21.
//
import SwiftUI
import MobileVLCKit
import Introspect
struct VideoPlayerViewRefactored: View {
@State private var shouldShowLoadingView: Bool = true;
var body: some View {
LoadingView(isShowing: $shouldShowLoadingView) {
Text("content")
}
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
.statusBar(hidden: true)
.prefersHomeIndicatorAutoHidden(true)
.preferredColorScheme(.dark)
.edgesIgnoringSafeArea(.all)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
}
}