Add Stinsen
Add ConnectToServerCoodinator Add HomeCoordinator Add LibraryListCoordinator Add MainCoordinator Add MainTabCoordinator
This commit is contained in:
parent
e677be7128
commit
3a090aaf4e
|
@ -141,7 +141,6 @@
|
|||
624C21752685CF60007F1390 /* SearchablePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 624C21742685CF60007F1390 /* SearchablePickerView.swift */; };
|
||||
625CB5682678B6FB00530A6E /* SplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5672678B6FB00530A6E /* SplashView.swift */; };
|
||||
625CB56A2678B71200530A6E /* SplashViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5692678B71200530A6E /* SplashViewModel.swift */; };
|
||||
625CB56C2678C0FD00530A6E /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB56B2678C0FD00530A6E /* MainTabView.swift */; };
|
||||
625CB56F2678C23300530A6E /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB56E2678C23300530A6E /* HomeView.swift */; };
|
||||
625CB5732678C32A00530A6E /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5722678C32A00530A6E /* HomeViewModel.swift */; };
|
||||
625CB5752678C33500530A6E /* LibraryListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5742678C33500530A6E /* LibraryListViewModel.swift */; };
|
||||
|
@ -166,6 +165,12 @@
|
|||
628B95382670CDAB0091AF3B /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 5377CBFF263B596B003A4E83 /* Model.xcdatamodeld */; };
|
||||
628B953A2670CE250091AF3B /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 628B95392670CE250091AF3B /* KeychainSwift */; };
|
||||
628B953C2670D2430091AF3B /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621338922660107500A81A2A /* StringExtensions.swift */; };
|
||||
62C29E9C26D0FE4200C1D2E7 /* Stinsen in Frameworks */ = {isa = PBXBuildFile; productRef = 62C29E9B26D0FE4200C1D2E7 /* Stinsen */; };
|
||||
62C29E9F26D1016600C1D2E7 /* MainCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29E9E26D1016600C1D2E7 /* MainCoordinator.swift */; };
|
||||
62C29EA126D102A500C1D2E7 /* MainTabCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29EA026D102A500C1D2E7 /* MainTabCoordinator.swift */; };
|
||||
62C29EA326D1030F00C1D2E7 /* ConnectToServerCoodinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29EA226D1030F00C1D2E7 /* ConnectToServerCoodinator.swift */; };
|
||||
62C29EA626D1036A00C1D2E7 /* HomeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29EA526D1036A00C1D2E7 /* HomeCoordinator.swift */; };
|
||||
62C29EA826D103D500C1D2E7 /* LibraryListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29EA726D103D500C1D2E7 /* LibraryListCoordinator.swift */; };
|
||||
62CB3F462685BAF7003D0A6F /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = 62CB3F452685BAF7003D0A6F /* Defaults */; };
|
||||
62CB3F482685BB3B003D0A6F /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = 62CB3F472685BB3B003D0A6F /* Defaults */; };
|
||||
62CB3F4B2685BB77003D0A6F /* DefaultsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62CB3F4A2685BB77003D0A6F /* DefaultsExtension.swift */; };
|
||||
|
@ -352,7 +357,6 @@
|
|||
624C21742685CF60007F1390 /* SearchablePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchablePickerView.swift; sourceTree = "<group>"; };
|
||||
625CB5672678B6FB00530A6E /* SplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashView.swift; sourceTree = "<group>"; };
|
||||
625CB5692678B71200530A6E /* SplashViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashViewModel.swift; sourceTree = "<group>"; };
|
||||
625CB56B2678C0FD00530A6E /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = "<group>"; };
|
||||
625CB56E2678C23300530A6E /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
||||
625CB5722678C32A00530A6E /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = "<group>"; };
|
||||
625CB5742678C33500530A6E /* LibraryListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListViewModel.swift; sourceTree = "<group>"; };
|
||||
|
@ -369,6 +373,11 @@
|
|||
628B952A2670CABE0091AF3B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
628B95362670CB800091AF3B /* JellyfinWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinWidget.swift; sourceTree = "<group>"; };
|
||||
628B953B2670D1FC0091AF3B /* WidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetExtension.entitlements; sourceTree = "<group>"; };
|
||||
62C29E9E26D1016600C1D2E7 /* MainCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainCoordinator.swift; sourceTree = "<group>"; };
|
||||
62C29EA026D102A500C1D2E7 /* MainTabCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabCoordinator.swift; sourceTree = "<group>"; };
|
||||
62C29EA226D1030F00C1D2E7 /* ConnectToServerCoodinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectToServerCoodinator.swift; sourceTree = "<group>"; };
|
||||
62C29EA526D1036A00C1D2E7 /* HomeCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCoordinator.swift; sourceTree = "<group>"; };
|
||||
62C29EA726D103D500C1D2E7 /* LibraryListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListCoordinator.swift; sourceTree = "<group>"; };
|
||||
62CB3F4A2685BB77003D0A6F /* DefaultsExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultsExtension.swift; sourceTree = "<group>"; };
|
||||
62E632D9267D2BC40063E547 /* LatestMediaViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LatestMediaViewModel.swift; sourceTree = "<group>"; };
|
||||
62E632DB267D2E130063E547 /* LibrarySearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibrarySearchViewModel.swift; sourceTree = "<group>"; };
|
||||
|
@ -419,6 +428,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
53649AAD269CFAEA00A2D8B7 /* Puppy in Frameworks */,
|
||||
62C29E9C26D0FE4200C1D2E7 /* Stinsen in Frameworks */,
|
||||
62CB3F462685BAF7003D0A6F /* Defaults in Frameworks */,
|
||||
5338F757263B7E2E0014BF09 /* KeychainSwift in Frameworks */,
|
||||
53EC6E25267EB10F006DD26A /* SwiftyJSON in Frameworks */,
|
||||
|
@ -632,6 +642,7 @@
|
|||
5377CBF3263B596A003A4E83 /* JellyfinPlayer */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
62C29E9D26D0FE5900C1D2E7 /* Coordinators */,
|
||||
53F866422687A45400DCD1D7 /* Components */,
|
||||
53AD124C2670278D0094A276 /* JellyfinPlayer.entitlements */,
|
||||
5377CBF8263B596B003A4E83 /* Assets.xcassets */,
|
||||
|
@ -660,7 +671,6 @@
|
|||
532E68CE267D9F6B007B9F13 /* VideoPlayerCastDeviceSelector.swift */,
|
||||
53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */,
|
||||
625CB5672678B6FB00530A6E /* SplashView.swift */,
|
||||
625CB56B2678C0FD00530A6E /* MainTabView.swift */,
|
||||
625CB56E2678C23300530A6E /* HomeView.swift */,
|
||||
);
|
||||
path = JellyfinPlayer;
|
||||
|
@ -746,6 +756,18 @@
|
|||
path = WidgetExtension;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
62C29E9D26D0FE5900C1D2E7 /* Coordinators */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
62C29E9E26D1016600C1D2E7 /* MainCoordinator.swift */,
|
||||
62C29EA026D102A500C1D2E7 /* MainTabCoordinator.swift */,
|
||||
62C29EA226D1030F00C1D2E7 /* ConnectToServerCoodinator.swift */,
|
||||
62C29EA526D1036A00C1D2E7 /* HomeCoordinator.swift */,
|
||||
62C29EA726D103D500C1D2E7 /* LibraryListCoordinator.swift */,
|
||||
);
|
||||
path = Coordinators;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
62EC352A26766657000E9F2D /* Singleton */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -850,6 +872,7 @@
|
|||
62CB3F452685BAF7003D0A6F /* Defaults */,
|
||||
53649AAC269CFAEA00A2D8B7 /* Puppy */,
|
||||
6260FFF826A09754003FA968 /* CombineExt */,
|
||||
62C29E9B26D0FE4200C1D2E7 /* Stinsen */,
|
||||
);
|
||||
productName = JellyfinPlayer;
|
||||
productReference = 5377CBF1263B596A003A4E83 /* JellyfinPlayer iOS.app */;
|
||||
|
@ -925,6 +948,7 @@
|
|||
53272533268BF9710035FBF1 /* XCRemoteSwiftPackageReference "SwiftUIFocusGuide" */,
|
||||
53649AAB269CFAEA00A2D8B7 /* XCRemoteSwiftPackageReference "Puppy" */,
|
||||
6260FFF726A09754003FA968 /* XCRemoteSwiftPackageReference "CombineExt" */,
|
||||
62C29E9A26D0FE4100C1D2E7 /* XCRemoteSwiftPackageReference "stinsen" */,
|
||||
);
|
||||
productRefGroup = 5377CBF2263B596A003A4E83 /* Products */;
|
||||
projectDirPath = "";
|
||||
|
@ -1165,11 +1189,14 @@
|
|||
53FF7F2A263CF3F500585C35 /* LatestMediaView.swift in Sources */,
|
||||
62E632EC267D410B0063E547 /* SeriesItemViewModel.swift in Sources */,
|
||||
625CB5732678C32A00530A6E /* HomeViewModel.swift in Sources */,
|
||||
62C29EA826D103D500C1D2E7 /* LibraryListCoordinator.swift in Sources */,
|
||||
62E632DC267D2E130063E547 /* LibrarySearchViewModel.swift in Sources */,
|
||||
5377CBFE263B596B003A4E83 /* PersistenceController.swift in Sources */,
|
||||
62C29E9F26D1016600C1D2E7 /* MainCoordinator.swift in Sources */,
|
||||
5389276E263C25100035E14B /* ContinueWatchingView.swift in Sources */,
|
||||
53F866442687A45F00DCD1D7 /* PortraitItemView.swift in Sources */,
|
||||
53AD124E26702B8A0094A276 /* SeasonItemView.swift in Sources */,
|
||||
62C29EA126D102A500C1D2E7 /* MainTabCoordinator.swift in Sources */,
|
||||
535BAE9F2649E569005FA86D /* ItemView.swift in Sources */,
|
||||
6225FCCB2663841E00E067F6 /* ParallaxHeader.swift in Sources */,
|
||||
625CB5772678C34300530A6E /* ConnectToServerViewModel.swift in Sources */,
|
||||
|
@ -1180,6 +1207,7 @@
|
|||
53F8377D265FF67C00F456B3 /* VideoPlayerSettingsView.swift in Sources */,
|
||||
53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */,
|
||||
62133890265F83A900A81A2A /* LibraryListView.swift in Sources */,
|
||||
62C29EA326D1030F00C1D2E7 /* ConnectToServerCoodinator.swift in Sources */,
|
||||
0959A5FD2686D29800C7C9A9 /* VideoUpNextView.swift in Sources */,
|
||||
62E632DA267D2BC40063E547 /* LatestMediaViewModel.swift in Sources */,
|
||||
625CB56F2678C23300530A6E /* HomeView.swift in Sources */,
|
||||
|
@ -1204,6 +1232,7 @@
|
|||
53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */,
|
||||
53E4E649263F725B00F67C6B /* MultiSelectorView.swift in Sources */,
|
||||
621338B32660A07800A81A2A /* LazyView.swift in Sources */,
|
||||
62C29EA626D1036A00C1D2E7 /* HomeCoordinator.swift in Sources */,
|
||||
531AC8BF26750DE20091C7EB /* ImageView.swift in Sources */,
|
||||
62E632E0267D30CA0063E547 /* LibraryViewModel.swift in Sources */,
|
||||
624C21752685CF60007F1390 /* SearchablePickerView.swift in Sources */,
|
||||
|
@ -1216,7 +1245,6 @@
|
|||
6267B3DA2671138200A7371D /* ImageExtensions.swift in Sources */,
|
||||
62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */,
|
||||
5389277C263CC3DB0035E14B /* BlurHashDecode.swift in Sources */,
|
||||
625CB56C2678C0FD00530A6E /* MainTabView.swift in Sources */,
|
||||
539B2DA5263BA5B8007FF1A4 /* SettingsView.swift in Sources */,
|
||||
5338F74E263B61370014BF09 /* ConnectToServerView.swift in Sources */,
|
||||
09389CC726819B4600AE350E /* VideoPlayerModel.swift in Sources */,
|
||||
|
@ -1718,6 +1746,14 @@
|
|||
kind = branch;
|
||||
};
|
||||
};
|
||||
62C29E9A26D0FE4100C1D2E7 /* XCRemoteSwiftPackageReference "stinsen" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/rundfunk47/stinsen";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 1.1.0;
|
||||
};
|
||||
};
|
||||
62CB3F442685BAF7003D0A6F /* XCRemoteSwiftPackageReference "Defaults" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/acvigue/Defaults";
|
||||
|
@ -1844,6 +1880,11 @@
|
|||
package = 5338F755263B7E2E0014BF09 /* XCRemoteSwiftPackageReference "keychain-swift" */;
|
||||
productName = KeychainSwift;
|
||||
};
|
||||
62C29E9B26D0FE4200C1D2E7 /* Stinsen */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 62C29E9A26D0FE4100C1D2E7 /* XCRemoteSwiftPackageReference "stinsen" */;
|
||||
productName = Stinsen;
|
||||
};
|
||||
62CB3F452685BAF7003D0A6F /* Defaults */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 62CB3F442685BAF7003D0A6F /* XCRemoteSwiftPackageReference "Defaults" */;
|
||||
|
|
|
@ -109,6 +109,15 @@
|
|||
"version": "0.3.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Stinsen",
|
||||
"repositoryURL": "https://github.com/rundfunk47/stinsen",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "e72c20b2c4bde0d6c3a911d4eda688fee7aa3bba",
|
||||
"version": "1.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "swift-log",
|
||||
"repositoryURL": "https://github.com/apple/swift-log.git",
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
*/
|
||||
|
||||
import SwiftUI
|
||||
import Stinsen
|
||||
|
||||
struct ConnectToServerView: View {
|
||||
@EnvironmentObject var main: ViewRouter<MainCoordinator.Route>
|
||||
@StateObject var viewModel = ConnectToServerViewModel()
|
||||
@State var username = ""
|
||||
@State var password = ""
|
||||
|
@ -59,6 +61,7 @@ struct ConnectToServerView: View {
|
|||
if SessionManager.current.doesUserHaveSavedSession(userID: publicUser.id!) {
|
||||
let user = SessionManager.current.getSavedSession(userID: publicUser.id!)
|
||||
SessionManager.current.loginWithSavedSession(user: user)
|
||||
main.route(to: .mainTab)
|
||||
} else {
|
||||
username = publicUser.name ?? ""
|
||||
viewModel.selectedPublicUser = publicUser
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
/*
|
||||
* SwiftFin is subject to the terms of the Mozilla Public
|
||||
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
final class ConnectToServerCoodinator: NavigationCoordinatable {
|
||||
var navigationStack = NavigationStack()
|
||||
|
||||
enum Route: NavigationRoute {}
|
||||
|
||||
func resolveRoute(route: Route) -> Transition {}
|
||||
|
||||
@ViewBuilder
|
||||
func start() -> some View {
|
||||
ConnectToServerView()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
/*
|
||||
* SwiftFin is subject to the terms of the Mozilla Public
|
||||
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
final class HomeCoordinator: NavigationCoordinatable {
|
||||
var navigationStack = NavigationStack()
|
||||
|
||||
enum Route: NavigationRoute {}
|
||||
|
||||
func resolveRoute(route: Route) -> Transition {}
|
||||
|
||||
@ViewBuilder
|
||||
func start() -> some View {
|
||||
HomeView()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
/*
|
||||
* SwiftFin is subject to the terms of the Mozilla Public
|
||||
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
final class LibraryListCoordinator: NavigationCoordinatable {
|
||||
var navigationStack = NavigationStack()
|
||||
|
||||
enum Route: NavigationRoute {}
|
||||
|
||||
func resolveRoute(route: Route) -> Transition {}
|
||||
|
||||
@ViewBuilder
|
||||
func start() -> some View {
|
||||
LibraryListView()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
/*
|
||||
* SwiftFin is subject to the terms of the Mozilla Public
|
||||
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
final class MainCoordinator: ViewCoordinatable {
|
||||
var children = ViewChild()
|
||||
|
||||
enum Route: ViewRoute {
|
||||
case mainTab
|
||||
case connectToServer
|
||||
}
|
||||
|
||||
func resolveRoute(route: Route) -> AnyCoordinatable {
|
||||
switch route {
|
||||
case .mainTab:
|
||||
return MainTabCoordinator().eraseToAnyCoordinatable()
|
||||
case .connectToServer:
|
||||
return NavigationViewCoordinator(ConnectToServerCoodinator()).eraseToAnyCoordinatable()
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func start() -> some View {
|
||||
SplashView()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
/*
|
||||
* SwiftFin is subject to the terms of the Mozilla Public
|
||||
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
import Stinsen
|
||||
|
||||
final class MainTabCoordinator: TabCoordinatable {
|
||||
lazy var children = TabChild(self, tabRoutes: [.home, .allMedia])
|
||||
|
||||
enum Route: TabRoute {
|
||||
case home
|
||||
case allMedia
|
||||
}
|
||||
|
||||
func tabItem(forTab tab: Int) -> some View {
|
||||
switch tab {
|
||||
case 0:
|
||||
Group {
|
||||
Text("Home")
|
||||
Image(systemName: "house")
|
||||
}
|
||||
case 1:
|
||||
Group {
|
||||
Text("Projects")
|
||||
Image(systemName: "folder")
|
||||
}
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
func resolveRoute(route: Route) -> AnyCoordinatable {
|
||||
switch route {
|
||||
case .home:
|
||||
return NavigationViewCoordinator(HomeCoordinator()).eraseToAnyCoordinatable()
|
||||
case .allMedia:
|
||||
return NavigationViewCoordinator(LibraryListCoordinator()).eraseToAnyCoordinatable()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ struct HomeView: View {
|
|||
ProgressView()
|
||||
} else {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading) {
|
||||
LazyVStack(alignment: .leading) {
|
||||
if !viewModel.resumeItems.isEmpty {
|
||||
ContinueWatchingView(items: viewModel.resumeItems)
|
||||
}
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import MessageUI
|
||||
import Defaults
|
||||
import MessageUI
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
// The notification we'll send when a shake gesture happens.
|
||||
extension UIDevice {
|
||||
|
@ -16,11 +17,11 @@ extension UIDevice {
|
|||
|
||||
// Override the default behavior of shake gestures to send our notification instead.
|
||||
extension UIWindow {
|
||||
open override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
|
||||
override open func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
|
||||
if motion == .motionShake {
|
||||
NotificationCenter.default.post(name: UIDevice.deviceDidShakeNotification, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A view modifier that detects shaking and calls a function of our choosing.
|
||||
|
@ -39,20 +40,20 @@ struct DeviceShakeViewModifier: ViewModifier {
|
|||
// A View extension to make the modifier easier to use.
|
||||
extension View {
|
||||
func onShake(perform action: @escaping () -> Void) -> some View {
|
||||
self.modifier(DeviceShakeViewModifier(action: action))
|
||||
modifier(DeviceShakeViewModifier(action: action))
|
||||
}
|
||||
}
|
||||
|
||||
extension UIDevice {
|
||||
var hasNotch: Bool {
|
||||
let bottom = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.safeAreaInsets.bottom ?? 0
|
||||
let bottom = UIApplication.shared.windows.filter { $0.isKeyWindow }.first?.safeAreaInsets.bottom ?? 0
|
||||
return bottom > 0
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func withHostingWindow(_ callback: @escaping (UIWindow?) -> Void) -> some View {
|
||||
self.background(HostingWindowFinder(callback: callback))
|
||||
background(HostingWindowFinder(callback: callback))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,8 +68,7 @@ struct HostingWindowFinder: UIViewRepresentable {
|
|||
return view
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: UIView, context: Context) {
|
||||
}
|
||||
func updateUIView(_ uiView: UIView, context: Context) {}
|
||||
}
|
||||
|
||||
struct PrefersHomeIndicatorAutoHiddenPreferenceKey: PreferenceKey {
|
||||
|
@ -105,18 +105,17 @@ 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
|
||||
}
|
||||
))
|
||||
.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) {
|
||||
@objc dynamic required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
super.modalPresentationStyle = .fullScreen
|
||||
}
|
||||
|
@ -131,6 +130,7 @@ class PreferenceUIHostingController: UIHostingController<AnyView> {
|
|||
public var _prefersHomeIndicatorAutoHidden = false {
|
||||
didSet { setNeedsUpdateOfHomeIndicatorAutoHidden() }
|
||||
}
|
||||
|
||||
override var prefersHomeIndicatorAutoHidden: Bool {
|
||||
_prefersHomeIndicatorAutoHidden
|
||||
}
|
||||
|
@ -146,6 +146,7 @@ class PreferenceUIHostingController: UIHostingController<AnyView> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
||||
_orientations
|
||||
}
|
||||
|
@ -176,7 +177,7 @@ extension View {
|
|||
|
||||
class EmailHelper: NSObject, MFMailComposeViewControllerDelegate {
|
||||
public static let shared = EmailHelper()
|
||||
private override init() {
|
||||
override private init() {
|
||||
//
|
||||
}
|
||||
|
||||
|
@ -192,7 +193,9 @@ class EmailHelper: NSObject, MFMailComposeViewControllerDelegate {
|
|||
let data = fileManager.contents(atPath: logURL.path)
|
||||
|
||||
picker.setSubject("[DEV-BUG] SwiftFin")
|
||||
picker.setMessageBody("Please don't edit this email.\n Please don't change the subject. \nUDID: \(UIDevice.current.identifierForVendor?.uuidString ?? "NIL")\n", isHTML: false)
|
||||
picker
|
||||
.setMessageBody("Please don't edit this email.\n Please don't change the subject. \nUDID: \(UIDevice.current.identifierForVendor?.uuidString ?? "NIL")\n",
|
||||
isHTML: false)
|
||||
picker.setToRecipients(["SwiftFin Bug Reports <swiftfin-bugs@jellyfin.org>"])
|
||||
picker.addAttachmentData(data!, mimeType: "text/plain", fileName: logURL.lastPathComponent)
|
||||
picker.mailComposeDelegate = self
|
||||
|
@ -218,13 +221,15 @@ struct JellyfinPlayerApp: App {
|
|||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
SplashView()
|
||||
EmptyView()
|
||||
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
||||
.onAppear(perform: {
|
||||
setupAppearance()
|
||||
})
|
||||
.withHostingWindow { window in
|
||||
window?.rootViewController = PreferenceUIHostingController(wrappedView: SplashView().environment(\.managedObjectContext, persistenceController.container.viewContext))
|
||||
window?
|
||||
.rootViewController = PreferenceUIHostingController(wrappedView: CoordinatorView(MainCoordinator())
|
||||
.environment(\.managedObjectContext, persistenceController.container.viewContext))
|
||||
}
|
||||
.onShake {
|
||||
EmailHelper.shared.sendLogs(logURL: LogManager.shared.logFileURL())
|
||||
|
@ -239,7 +244,6 @@ struct JellyfinPlayerApp: App {
|
|||
}
|
||||
|
||||
class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
|
||||
static var orientationLock = UIInterfaceOrientationMask.all
|
||||
|
||||
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
//
|
||||
/*
|
||||
* SwiftFin is subject to the terms of the Mozilla Public
|
||||
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct MainTabView: View {
|
||||
@State private var tabSelection: Tab = .home
|
||||
|
||||
var body: some View {
|
||||
TabView(selection: $tabSelection) {
|
||||
NavigationView {
|
||||
HomeView()
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.tabItem {
|
||||
Text("Home")
|
||||
Image(systemName: "house")
|
||||
}
|
||||
.tag(Tab.home)
|
||||
NavigationView {
|
||||
LibraryListView()
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.tabItem {
|
||||
Text("All Media")
|
||||
Image(systemName: "folder")
|
||||
}
|
||||
.tag(Tab.allMedia)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension MainTabView {
|
||||
|
||||
enum Tab: String {
|
||||
case home
|
||||
case allMedia
|
||||
}
|
||||
}
|
|
@ -6,10 +6,12 @@
|
|||
*/
|
||||
|
||||
import CoreData
|
||||
import SwiftUI
|
||||
import Defaults
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsView: View {
|
||||
@EnvironmentObject var main: ViewRouter<MainCoordinator.Route>
|
||||
@Environment(\.managedObjectContext) private var viewContext
|
||||
|
||||
@ObservedObject var viewModel: SettingsViewModel
|
||||
|
@ -44,13 +46,13 @@ struct SettingsView: View {
|
|||
Text(bitrate.name).tag(bitrate.value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Picker("Jump Forward Length", selection: $jumpForwardLength) {
|
||||
ForEach(self.viewModel.videoPlayerJumpLengths, id: \.self) { length in
|
||||
Text(length.label).tag(length.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Picker("Jump Backward Length", selection: $jumpBackwardLength) {
|
||||
ForEach(self.viewModel.videoPlayerJumpLengths, id: \.self) { length in
|
||||
Text(length.label).tag(length.rawValue)
|
||||
|
@ -63,19 +65,22 @@ struct SettingsView: View {
|
|||
SearchablePicker(label: "Preferred subtitle language",
|
||||
options: viewModel.langs,
|
||||
optionToString: { $0.name },
|
||||
selected: Binding<TrackLanguage>(
|
||||
get: { viewModel.langs.first(where: { $0.isoCode == autoSelectSubtitlesLangcode }) ?? .auto },
|
||||
set: {autoSelectSubtitlesLangcode = $0.isoCode}
|
||||
)
|
||||
)
|
||||
selected: Binding<TrackLanguage>(get: {
|
||||
viewModel.langs
|
||||
.first(where: { $0.isoCode == autoSelectSubtitlesLangcode
|
||||
}) ??
|
||||
.auto
|
||||
},
|
||||
set: { autoSelectSubtitlesLangcode = $0.isoCode }))
|
||||
SearchablePicker(label: "Preferred audio language",
|
||||
options: viewModel.langs,
|
||||
optionToString: { $0.name },
|
||||
selected: Binding<TrackLanguage>(
|
||||
get: { viewModel.langs.first(where: { $0.isoCode == autoSelectAudioLangcode }) ?? .auto },
|
||||
set: { autoSelectAudioLangcode = $0.isoCode}
|
||||
)
|
||||
)
|
||||
selected: Binding<TrackLanguage>(get: {
|
||||
viewModel.langs
|
||||
.first(where: { $0.isoCode == autoSelectAudioLangcode }) ??
|
||||
.auto
|
||||
},
|
||||
set: { autoSelectAudioLangcode = $0.isoCode }))
|
||||
Picker(NSLocalizedString("Appearance", comment: ""), selection: $appAppearance) {
|
||||
ForEach(self.viewModel.appearances, id: \.self) { appearance in
|
||||
Text(appearance.localizedName).tag(appearance.rawValue)
|
||||
|
@ -92,22 +97,16 @@ struct SettingsView: View {
|
|||
Spacer()
|
||||
Button {
|
||||
print("logging out")
|
||||
main.route(to: .connectToServer)
|
||||
close = false
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
let nc = NotificationCenter.default
|
||||
nc.post(name: Notification.Name("didSignOut"), object: nil)
|
||||
}
|
||||
} label: {
|
||||
Text("Switch user").font(.callout)
|
||||
}
|
||||
}
|
||||
Button {
|
||||
SessionManager.current.logout()
|
||||
main.route(to: .connectToServer)
|
||||
close = false
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
SessionManager.current.logout()
|
||||
let nc = NotificationCenter.default
|
||||
nc.post(name: Notification.Name("didSignOut"), object: nil)
|
||||
}
|
||||
} label: {
|
||||
Text("Sign out").font(.callout)
|
||||
}
|
||||
|
|
|
@ -7,19 +7,21 @@
|
|||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
struct SplashView: View {
|
||||
@EnvironmentObject var main: ViewRouter<MainCoordinator.Route>
|
||||
@StateObject var viewModel = SplashViewModel()
|
||||
|
||||
var body: some View {
|
||||
if viewModel.isLoggedIn {
|
||||
MainTabView()
|
||||
} else {
|
||||
NavigationView {
|
||||
ConnectToServerView()
|
||||
ProgressView()
|
||||
.onReceive(viewModel.$isLoggedIn) { flag in
|
||||
if flag {
|
||||
main.route(to: .mainTab)
|
||||
} else {
|
||||
main.route(to: .connectToServer)
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import Combine
|
|||
import GoogleCast
|
||||
import SwiftyJSON
|
||||
import Defaults
|
||||
import Stinsen
|
||||
|
||||
enum PlayerDestination {
|
||||
case remote
|
||||
|
@ -27,6 +28,9 @@ protocol PlayerViewControllerDelegate: AnyObject {
|
|||
|
||||
class PlayerViewController: UIViewController, GCKDiscoveryManagerListener, GCKRemoteMediaClientListener {
|
||||
|
||||
@RouterObject
|
||||
var main: ViewRouter<MainCoordinator.Route>?
|
||||
|
||||
weak var delegate: PlayerViewControllerDelegate?
|
||||
|
||||
var cancellables = Set<AnyCancellable>()
|
||||
|
@ -508,6 +512,7 @@ class PlayerViewController: UIViewController, GCKDiscoveryManagerListener, GCKRe
|
|||
case .error(401, _, _, _):
|
||||
self.delegate?.exitPlayer(self)
|
||||
SessionManager.current.logout()
|
||||
main?.route(to: .connectToServer)
|
||||
case .error:
|
||||
self.delegate?.exitPlayer(self)
|
||||
}
|
||||
|
|
|
@ -10,8 +10,11 @@
|
|||
import Combine
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
import Stinsen
|
||||
|
||||
final class ConnectToServerViewModel: ViewModel {
|
||||
@RouterObject
|
||||
var main: ViewRouter<MainCoordinator.Route>?
|
||||
|
||||
@Published var isConnectedServer = false
|
||||
|
||||
|
@ -23,13 +26,14 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
@Published var publicUsers = [UserDto]()
|
||||
@Published var selectedPublicUser = UserDto()
|
||||
|
||||
private let discovery: ServerDiscovery = ServerDiscovery()
|
||||
private let discovery = ServerDiscovery()
|
||||
@Published var servers: [ServerDiscovery.ServerLookupResponse] = []
|
||||
@Published var searching = false
|
||||
|
||||
func getPublicUsers() {
|
||||
if ServerEnvironment.current.server != nil {
|
||||
LogManager.shared.log.debug("Attempting to read public users from \(ServerEnvironment.current.server.baseURI!)", tag: "getPublicUsers")
|
||||
LogManager.shared.log.debug("Attempting to read public users from \(ServerEnvironment.current.server.baseURI!)",
|
||||
tag: "getPublicUsers")
|
||||
UserAPI.getPublicUsers()
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { completion in
|
||||
|
@ -46,12 +50,12 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
}
|
||||
|
||||
func hidePublicUsers() {
|
||||
self.lastPublicUsers = publicUsers
|
||||
lastPublicUsers = publicUsers
|
||||
publicUsers = []
|
||||
}
|
||||
|
||||
func showPublicUsers() {
|
||||
self.publicUsers = lastPublicUsers
|
||||
publicUsers = lastPublicUsers
|
||||
lastPublicUsers = []
|
||||
}
|
||||
|
||||
|
@ -60,7 +64,8 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
ServerEnvironment.current.create(with: uriSubject.value)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { completion in
|
||||
self.handleAPIRequestError(displayMessage: "Unable to connect to server.", logLevel: .critical, tag: "connectToServer", completion: completion)
|
||||
self.handleAPIRequestError(displayMessage: "Unable to connect to server.", logLevel: .critical, tag: "connectToServer",
|
||||
completion: completion)
|
||||
}, receiveValue: { _ in
|
||||
LogManager.shared.log.debug("Connected to server at \"\(self.uriSubject.value)\"", tag: "connectToServer")
|
||||
self.getPublicUsers()
|
||||
|
@ -70,7 +75,7 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
|
||||
func connectToServer(at url: URL) {
|
||||
uriSubject.send(url.absoluteString)
|
||||
self.connectToServer()
|
||||
connectToServer()
|
||||
}
|
||||
|
||||
func discoverServers() {
|
||||
|
@ -81,7 +86,7 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
self.searching = false
|
||||
}
|
||||
|
||||
discovery.locateServer { [self] (server) in
|
||||
discovery.locateServer { [self] server in
|
||||
if let server = server, !servers.contains(server) {
|
||||
servers.append(server)
|
||||
}
|
||||
|
@ -91,13 +96,16 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
|
||||
func login() {
|
||||
LogManager.shared.log.debug("Attempting to login to server at \"\(uriSubject.value)\"", tag: "login")
|
||||
LogManager.shared.log.debug("username == \"\": \(usernameSubject.value.isEmpty), password == \"\": \(passwordSubject.value.isEmpty)", tag: "login")
|
||||
LogManager.shared.log
|
||||
.debug("username == \"\": \(usernameSubject.value.isEmpty), password == \"\": \(passwordSubject.value.isEmpty)",
|
||||
tag: "login")
|
||||
SessionManager.current.login(username: usernameSubject.value, password: passwordSubject.value)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { completion in
|
||||
self.handleAPIRequestError(displayMessage: "Unable to connect to server.", logLevel: .critical, tag: "login", completion: completion)
|
||||
}, receiveValue: { _ in
|
||||
|
||||
self.handleAPIRequestError(displayMessage: "Unable to connect to server.", logLevel: .critical, tag: "login",
|
||||
completion: completion)
|
||||
}, receiveValue: { [weak self] _ in
|
||||
self?.main?.route(to: .mainTab)
|
||||
})
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue