// // JellyfinPlayerApp.swift // JellyfinPlayer // // Created by Aiden Vigue on 4/29/21. // import SwiftUI 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 return bottom > 0 } } class OrientationInfo: ObservableObject { enum Orientation { case portrait case landscape } @Published var orientation: Orientation = .portrait; private var _observer: NSObjectProtocol? init() { _observer = NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: nil) { [weak self] note in guard let device = note.object as? UIDevice else { return } if device.orientation.isPortrait { self?.orientation = .portrait } else if device.orientation.isLandscape { self?.orientation = .landscape } } } deinit { if let observer = _observer { NotificationCenter.default.removeObserver(observer) } } } 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 { init(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) } } extension String { public func leftPad(toWidth width: Int, withString string: String?) -> String { let paddingString = string ?? " " if self.count >= width { return self } let remainingLength: Int = width - self.count var padString = String() for _ in 0 ..< remainingLength { padString += paddingString } return "\(padString)\(self)" } } @main struct JellyfinPlayerApp: App { let persistenceController = PersistenceController.shared @StateObject private var jsi = justSignedIn() var body: some Scene { WindowGroup { ContentView() .environment(\.managedObjectContext, persistenceController.container.viewContext) .environmentObject(OrientationInfo()) .environmentObject(jsi) .withHostingWindow() { window in window?.rootViewController = PreferenceUIHostingController(wrappedView: ContentView().environment(\.managedObjectContext, persistenceController.container.viewContext).environmentObject(OrientationInfo()).environmentObject(jsi)) } } } }