Start flattening hierarchy
This commit is contained in:
parent
3cefdb2ad4
commit
eb895a0805
|
@ -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 */
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,7 +89,6 @@ struct NextUpView: View {
|
|||
}
|
||||
.frame(width: 100, height: 150)
|
||||
.cornerRadius(10)
|
||||
.shadow(radius: 6)
|
||||
Text(item.SeriesName ?? "")
|
||||
.font(.caption)
|
||||
.fontWeight(.semibold)
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import SwiftUI
|
||||
import SwiftyRequest
|
||||
import SwiftyJSON
|
||||
import Introspect
|
||||
import SDWebImageSwiftUI
|
||||
|
||||
struct SeasonItemView: View {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue