i hate mvvm
This commit is contained in:
parent
f4008c8c15
commit
f1204238c0
|
@ -17,11 +17,19 @@ struct ConnectToServerView: View {
|
|||
VStack(alignment: .leading) {
|
||||
if viewModel.isConnectedServer {
|
||||
if viewModel.publicUsers.isEmpty {
|
||||
Section(header: Text(viewModel.lastPublicUsers.isEmpty || viewModel.username == "" ? "Login to \(ServerEnvironment.current.server.name ?? "")": "Password for \(viewModel.username)")) {
|
||||
Section(header: Text(viewModel.lastPublicUsers.isEmpty || viewModel.username == "" ? "Login to \(ServerEnvironment.current.server.name ?? "")": "")) {
|
||||
if(viewModel.lastPublicUsers.isEmpty || viewModel.username == "") {
|
||||
TextField("Username", text: $viewModel.username)
|
||||
.disableAutocorrection(true)
|
||||
.autocapitalization(.none)
|
||||
} else {
|
||||
HStack() {
|
||||
Spacer()
|
||||
ImageView(src: URL(string: "\(ServerEnvironment.current.server.baseURI ?? "")/Users/\(viewModel.selectedPublicUser.id ?? "")/Images/Primary?width=500&quality=80&tag=\(viewModel.selectedPublicUser.primaryImageTag ?? "")")!)
|
||||
.frame(width: 250, height: 250)
|
||||
.cornerRadius(125.0)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
SecureField("Password (optional)", text: $viewModel.password)
|
||||
.disableAutocorrection(true)
|
||||
|
@ -57,18 +65,22 @@ struct ConnectToServerView: View {
|
|||
Spacer()
|
||||
}.disabled(viewModel.isLoading || viewModel.username.isEmpty)
|
||||
}
|
||||
Text("Logging in will link this user to your Apple TV profile").font(.caption).foregroundColor(.secondary)
|
||||
}
|
||||
} else {
|
||||
VStack() {
|
||||
HStack() {
|
||||
ForEach(viewModel.publicUsers, id: \.id) { publicUser in
|
||||
Button(action: {
|
||||
viewModel.username = publicUser.name ?? ""
|
||||
viewModel.hidePublicUsers()
|
||||
if !(publicUser.hasPassword ?? true) {
|
||||
viewModel.password = ""
|
||||
viewModel.login()
|
||||
if(!viewModel.userHasSavedCredentials(userID: publicUser.id!)) {
|
||||
viewModel.username = publicUser.name ?? ""
|
||||
viewModel.selectedPublicUser = publicUser
|
||||
viewModel.hidePublicUsers()
|
||||
if !(publicUser.hasPassword ?? true) {
|
||||
viewModel.password = ""
|
||||
viewModel.login()
|
||||
}
|
||||
} else {
|
||||
viewModel.loginWithSavedCredentials(user: publicUser)
|
||||
}
|
||||
}) {
|
||||
VStack {
|
||||
|
@ -124,6 +136,8 @@ struct ConnectToServerView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.padding(.leading, 90)
|
||||
.padding(.trailing, 90)
|
||||
.alert(item: $viewModel.errorMessage) { _ in
|
||||
Alert(title: Text("Error"), message: Text(viewModel.errorMessage ?? ""), dismissButton: .default(Text("Ok")))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
/*
|
||||
* 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 SwiftUI
|
||||
import JellyfinAPI
|
||||
|
||||
fileprivate struct ProgressBar: Shape {
|
||||
func path(in rect: CGRect) -> Path {
|
||||
var path = Path()
|
||||
|
||||
let tl = CGPoint(x: rect.minX, y: rect.minY)
|
||||
let tr = CGPoint(x: rect.maxX, y: rect.minY)
|
||||
let br = CGPoint(x: rect.maxX, y: rect.maxY)
|
||||
let bls = CGPoint(x: rect.minX + 10, y: rect.maxY)
|
||||
let blc = CGPoint(x: rect.minX + 10, y: rect.maxY - 10)
|
||||
|
||||
path.move(to: tl)
|
||||
path.addLine(to: tr)
|
||||
path.addLine(to: br)
|
||||
path.addLine(to: bls)
|
||||
path.addRelativeArc(center: blc, radius: 10,
|
||||
startAngle: Angle.degrees(90), delta: Angle.degrees(90))
|
||||
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
struct ContinueWatchingItem: View {
|
||||
@Environment(\.isFocused) var envFocused: Bool
|
||||
@State var focused: Bool = false;
|
||||
|
||||
var item: BaseItemDto;
|
||||
|
||||
var body: some View {
|
||||
VStack() {
|
||||
ImageView(src: item.getBackdropImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 375), bh: item.getBackdropImageBlurHash())
|
||||
.frame(width: 375, height: 250)
|
||||
.cornerRadius(10)
|
||||
.overlay(
|
||||
Rectangle()
|
||||
.fill(Color(red: 172/255, green: 92/255, blue: 195/255))
|
||||
.mask(ProgressBar())
|
||||
.frame(width: CGFloat(item.userData?.playedPercentage ?? 0 * 3.75), height: 12)
|
||||
.padding(6), alignment: .bottomLeading
|
||||
)
|
||||
if(focused) {
|
||||
Text(item.seriesName ?? item.name ?? "")
|
||||
.font(.callout)
|
||||
.fontWeight(.semibold)
|
||||
.lineLimit(1)
|
||||
.frame(width: 375)
|
||||
} else {
|
||||
Spacer().frame(height: 25)
|
||||
}
|
||||
}
|
||||
.onChange(of: envFocused) { envFocus in
|
||||
withAnimation(.linear(duration: 0.15)) {
|
||||
self.focused = envFocus
|
||||
}
|
||||
}
|
||||
.scaleEffect(focused ? 1.1 : 1)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* JellyfinPlayer/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 SwiftUI
|
||||
import JellyfinAPI
|
||||
import Combine
|
||||
|
||||
struct ContinueWatchingView: View {
|
||||
var items: [BaseItemDto]
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
if items.count > 0 {
|
||||
Text("Continue Watching")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
.padding(.leading, 135)
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
LazyHStack {
|
||||
Spacer().frame(width: 90)
|
||||
ForEach(items, id: \.id) { item in
|
||||
NavigationLink(destination: Text("itemv")) {
|
||||
ContinueWatchingItem(item: item)
|
||||
}.buttonStyle(PlainNavigationLinkButtonStyle())
|
||||
}
|
||||
Spacer().frame(width: 90)
|
||||
}
|
||||
}.frame(height: 330)
|
||||
.offset(y: -10)
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
/*
|
||||
* 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 HomeView: View {
|
||||
@StateObject var viewModel = HomeViewModel()
|
||||
|
||||
@State var showingSettings = false
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
if viewModel.isLoading {
|
||||
ProgressView()
|
||||
} else {
|
||||
LazyVStack(alignment: .leading) {
|
||||
if !viewModel.resumeItems.isEmpty {
|
||||
ContinueWatchingView(items: viewModel.resumeItems)
|
||||
}
|
||||
/*
|
||||
if !viewModel.nextUpItems.isEmpty {
|
||||
NextUpView(items: viewModel.nextUpItems)
|
||||
}
|
||||
if !viewModel.librariesShowRecentlyAddedIDs.isEmpty {
|
||||
ForEach(viewModel.librariesShowRecentlyAddedIDs, id: \.self) { libraryID in
|
||||
VStack(alignment: .leading) {
|
||||
let library = viewModel.libraries.first(where: { $0.id == libraryID })
|
||||
HStack {
|
||||
Text("Latest \(library?.name ?? "")")
|
||||
.font(.title2)
|
||||
.fontWeight(.bold)
|
||||
.padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 16))
|
||||
Spacer()
|
||||
NavigationLink(destination: LazyView {
|
||||
//LibraryView(usingParentID: libraryID, title: library?.name ?? "", usingFilters: viewModel.recentFilterSet)
|
||||
Text("library here")
|
||||
}) {
|
||||
HStack {
|
||||
Text("See All").font(.subheadline).fontWeight(.bold)
|
||||
Image(systemName: "chevron.right").font(Font.subheadline.bold())
|
||||
}
|
||||
}
|
||||
}.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
|
||||
//LatestMediaView(usingParentID: libraryID)
|
||||
}.padding(EdgeInsets(top: 4, leading: 0, bottom: 0, trailing: 0))
|
||||
}
|
||||
}
|
||||
|
||||
Spacer().frame(height: 16)
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
struct ImageButtonStyle: ButtonStyle {
|
||||
|
||||
let focused: Bool
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
configuration
|
||||
.label
|
||||
.padding(6)
|
||||
.foregroundColor(Color.white)
|
||||
.background(Color.blue)
|
||||
.cornerRadius(100)
|
||||
.shadow(color: .black, radius: self.focused ? 20 : 0, x: 0, y: 0) // 0
|
||||
|
||||
}
|
||||
}
|
|
@ -7,5 +7,9 @@
|
|||
<string>get-current-user</string>
|
||||
<string>runs-as-current-user</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)me.vigue.jellyfin.sharedKeychain</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -15,6 +15,8 @@ struct JellyfinPlayer_tvOSApp: App {
|
|||
WindowGroup {
|
||||
SplashView()
|
||||
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
||||
.padding(EdgeInsets(top: 0, leading: -90, bottom: 0, trailing: -90))
|
||||
.ignoresSafeArea(.all)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
/*
|
||||
* 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) {
|
||||
HomeView()
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.tabItem {
|
||||
Text(Tab.home.localized)
|
||||
Image(systemName: "house")
|
||||
}
|
||||
.tag(Tab.home)
|
||||
|
||||
Text("Library")
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.tabItem {
|
||||
Text(Tab.allMedia.localized)
|
||||
Image(systemName: "folder")
|
||||
}
|
||||
.tag(Tab.allMedia)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension MainTabView {
|
||||
enum Tab: String {
|
||||
case home
|
||||
case allMedia
|
||||
|
||||
var localized: String {
|
||||
switch self {
|
||||
case .home:
|
||||
return "Home"
|
||||
case .allMedia:
|
||||
return "All Media"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/* JellyfinPlayer/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 SwiftUI
|
||||
import Combine
|
||||
import JellyfinAPI
|
||||
|
||||
struct NextUpView: View {
|
||||
|
||||
var items: [BaseItemDto]
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
if items.count != 0 {
|
||||
Text("Next Up")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
LazyHStack {
|
||||
Spacer().frame(width: 16)
|
||||
ForEach(items, id: \.id) { item in
|
||||
NavigationLink(destination: EmptyView()) {
|
||||
VStack(alignment: .leading) {
|
||||
ImageView(src: item.getSeriesPrimaryImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 100), bh: item.getSeriesPrimaryImageBlurHash())
|
||||
.frame(width: 100, height: 150)
|
||||
.cornerRadius(10)
|
||||
Spacer().frame(height: 5)
|
||||
Text(item.seriesName!)
|
||||
.font(.caption)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundColor(.primary)
|
||||
.lineLimit(1)
|
||||
Text("S\(item.parentIndexNumber ?? 0):E\(item.indexNumber ?? 0)")
|
||||
.font(.caption)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundColor(.secondary)
|
||||
.lineLimit(1)
|
||||
}.frame(width: 100)
|
||||
Spacer().frame(width: 16)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(height: 200)
|
||||
}
|
||||
}
|
||||
.padding(EdgeInsets(top: 4, leading: 0, bottom: 0, trailing: 0))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import SwiftUI
|
||||
|
||||
struct PlainNavigationLinkButtonStyle: ButtonStyle {
|
||||
func makeBody(configuration: Self.Configuration) -> some View {
|
||||
PlainNavigationLinkButton(configuration: configuration)
|
||||
}
|
||||
}
|
||||
|
||||
struct PlainNavigationLinkButton: View {
|
||||
let configuration: ButtonStyle.Configuration
|
||||
|
||||
var body: some View {
|
||||
configuration.label
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
# Design Notes
|
||||
|
||||
tvos is dumb and how I got around the ScrollViews clipping requires ALL interface elements to have a leading and trailing padding of 135 pt to align with the original "safe area bounds"
|
||||
|
|
@ -11,15 +11,28 @@ import SwiftUI
|
|||
|
||||
struct SplashView: View {
|
||||
@StateObject var viewModel = SplashViewModel()
|
||||
|
||||
@State var showingAlert: Bool = false
|
||||
|
||||
var body: some View {
|
||||
if viewModel.isLoggedIn {
|
||||
Text("home")
|
||||
} else {
|
||||
NavigationView {
|
||||
ConnectToServerView(isLoggedIn: $viewModel.isLoggedIn)
|
||||
Group {
|
||||
if viewModel.isLoggedIn {
|
||||
NavigationView() {
|
||||
MainTabView()
|
||||
}
|
||||
.padding(.leading, -60)
|
||||
.padding(.trailing, -60)
|
||||
} else {
|
||||
NavigationView {
|
||||
ConnectToServerView(isLoggedIn: $viewModel.isLoggedIn)
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
.alert(isPresented: $showingAlert) {
|
||||
Alert(title: Text("Important message"), message: Text("\(ServerEnvironment.current.errorMessage)"), dismissButton: .default(Text("Got it!")))
|
||||
}
|
||||
.onChange(of: ServerEnvironment.current.hasErrorMessage) { hEM in
|
||||
self.showingAlert = hEM
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,17 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
531690E5267ABD5C005D8AB9 /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690E4267ABD5C005D8AB9 /* MainTabView.swift */; };
|
||||
531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690E6267ABD79005D8AB9 /* HomeView.swift */; };
|
||||
531690EC267ABF46005D8AB9 /* ContinueWatchingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690EB267ABF46005D8AB9 /* ContinueWatchingView.swift */; };
|
||||
531690ED267ABF46005D8AB9 /* ContinueWatchingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690EB267ABF46005D8AB9 /* ContinueWatchingView.swift */; };
|
||||
531690EF267ABF72005D8AB9 /* NextUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690EE267ABF72005D8AB9 /* NextUpView.swift */; };
|
||||
531690F0267ABF72005D8AB9 /* NextUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690EE267ABF72005D8AB9 /* NextUpView.swift */; };
|
||||
531690F7267ACC00005D8AB9 /* ContinueWatchingItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690F6267ACC00005D8AB9 /* ContinueWatchingItem.swift */; };
|
||||
531690FA267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690F9267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift */; };
|
||||
531690FD267AEDC5005D8AB9 /* HandleAPIRequestCompletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690FC267AEDC5005D8AB9 /* HandleAPIRequestCompletion.swift */; };
|
||||
531690FE267AEDC5005D8AB9 /* HandleAPIRequestCompletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690FC267AEDC5005D8AB9 /* HandleAPIRequestCompletion.swift */; };
|
||||
531690FF267AEDC5005D8AB9 /* HandleAPIRequestCompletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690FC267AEDC5005D8AB9 /* HandleAPIRequestCompletion.swift */; };
|
||||
53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */; };
|
||||
531ABF6C2671F5CC00C0FE20 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 628B95212670CABD0091AF3B /* WidgetKit.framework */; };
|
||||
531AC8BF26750DE20091C7EB /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531AC8BE26750DE20091C7EB /* ImageView.swift */; };
|
||||
|
@ -171,6 +182,14 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
531690E4267ABD5C005D8AB9 /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = "<group>"; };
|
||||
531690E6267ABD79005D8AB9 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
||||
531690EB267ABF46005D8AB9 /* ContinueWatchingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContinueWatchingView.swift; sourceTree = "<group>"; };
|
||||
531690EE267ABF72005D8AB9 /* NextUpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextUpView.swift; sourceTree = "<group>"; };
|
||||
531690F6267ACC00005D8AB9 /* ContinueWatchingItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContinueWatchingItem.swift; sourceTree = "<group>"; };
|
||||
531690F8267AD135005D8AB9 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
531690F9267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlainNavigationLinkButton.swift; sourceTree = "<group>"; };
|
||||
531690FC267AEDC5005D8AB9 /* HandleAPIRequestCompletion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleAPIRequestCompletion.swift; sourceTree = "<group>"; };
|
||||
53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceProfileBuilder.swift; sourceTree = "<group>"; };
|
||||
531AC8BE26750DE20091C7EB /* ImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = "<group>"; };
|
||||
5321753A2671BCFC005491E6 /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
|
||||
|
@ -288,6 +307,23 @@
|
|||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
531690F5267ACBF2005D8AB9 /* ContinueWatching */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
531690EB267ABF46005D8AB9 /* ContinueWatchingView.swift */,
|
||||
531690F6267ACC00005D8AB9 /* ContinueWatchingItem.swift */,
|
||||
);
|
||||
path = ContinueWatching;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
531690FB267AD7FA005D8AB9 /* NextUp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
531690EE267ABF72005D8AB9 /* NextUpView.swift */,
|
||||
);
|
||||
path = NextUp;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
532175392671BCED005491E6 /* ViewModels */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -304,7 +340,10 @@
|
|||
535870612669D21600D05A09 /* JellyfinPlayer tvOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
531690FB267AD7FA005D8AB9 /* NextUp */,
|
||||
531690F5267ACBF2005D8AB9 /* ContinueWatching */,
|
||||
53ABFDDA267972BF00886593 /* JellyfinPlayer tvOS.entitlements */,
|
||||
531690F9267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift */,
|
||||
535870622669D21600D05A09 /* JellyfinPlayer_tvOSApp.swift */,
|
||||
535870662669D21700D05A09 /* Assets.xcassets */,
|
||||
5358706B2669D21700D05A09 /* PersistenceController.swift */,
|
||||
|
@ -312,6 +351,9 @@
|
|||
535870682669D21700D05A09 /* Preview Content */,
|
||||
53ABFDDD267974E300886593 /* SplashView.swift */,
|
||||
53ABFDEA2679753200886593 /* ConnectToServerView.swift */,
|
||||
531690E4267ABD5C005D8AB9 /* MainTabView.swift */,
|
||||
531690E6267ABD79005D8AB9 /* HomeView.swift */,
|
||||
531690F8267AD135005D8AB9 /* README.md */,
|
||||
);
|
||||
path = "JellyfinPlayer tvOS";
|
||||
sourceTree = "<group>";
|
||||
|
@ -348,12 +390,12 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
53D5E3DA264B460200BADDC8 /* Cartfile */,
|
||||
628B95252670CABD0091AF3B /* WidgetExtension */,
|
||||
53D5E3DB264B47EE00BADDC8 /* Frameworks */,
|
||||
5377CBF3263B596A003A4E83 /* JellyfinPlayer */,
|
||||
535870612669D21600D05A09 /* JellyfinPlayer tvOS */,
|
||||
5377CBF2263B596A003A4E83 /* Products */,
|
||||
535870752669D60C00D05A09 /* Shared */,
|
||||
628B95252670CABD0091AF3B /* WidgetExtension */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
|
@ -436,6 +478,7 @@
|
|||
6267B3D526710B8900A7371D /* CollectionExtensions.swift */,
|
||||
6267B3D92671138200A7371D /* ImageExtensions.swift */,
|
||||
62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */,
|
||||
531690FC267AEDC5005D8AB9 /* HandleAPIRequestCompletion.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
|
@ -638,18 +681,24 @@
|
|||
6267B3DC2671139500A7371D /* ImageExtensions.swift in Sources */,
|
||||
53ABFDE9267974EF00886593 /* HomeViewModel.swift in Sources */,
|
||||
62EC352D26766675000E9F2D /* ServerEnvironment.swift in Sources */,
|
||||
531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */,
|
||||
53ABFDDE267974E300886593 /* SplashView.swift in Sources */,
|
||||
53ABFDE8267974EF00886593 /* SplashViewModel.swift in Sources */,
|
||||
531690ED267ABF46005D8AB9 /* ContinueWatchingView.swift in Sources */,
|
||||
62EC3530267666A5000E9F2D /* SessionManager.swift in Sources */,
|
||||
531690F7267ACC00005D8AB9 /* ContinueWatchingItem.swift in Sources */,
|
||||
535870A82669D8AE00D05A09 /* StringExtensions.swift in Sources */,
|
||||
53ABFDE6267974EF00886593 /* SettingsViewModel.swift in Sources */,
|
||||
6267B3D826710B9800A7371D /* CollectionExtensions.swift in Sources */,
|
||||
535870A52669D8AE00D05A09 /* ParallaxHeader.swift in Sources */,
|
||||
531690F0267ABF72005D8AB9 /* NextUpView.swift in Sources */,
|
||||
535870A72669D8AE00D05A09 /* MultiSelectorView.swift in Sources */,
|
||||
531690E5267ABD5C005D8AB9 /* MainTabView.swift in Sources */,
|
||||
53ABFDE7267974EF00886593 /* ConnectToServerViewModel.swift in Sources */,
|
||||
53ABFDEE26799DCD00886593 /* ImageView.swift in Sources */,
|
||||
5358706C2669D21700D05A09 /* PersistenceController.swift in Sources */,
|
||||
535870AA2669D8AE00D05A09 /* BlurHashDecode.swift in Sources */,
|
||||
531690FE267AEDC5005D8AB9 /* HandleAPIRequestCompletion.swift in Sources */,
|
||||
53ABFDE5267974EF00886593 /* ViewModel.swift in Sources */,
|
||||
535870A62669D8AE00D05A09 /* LazyView.swift in Sources */,
|
||||
5321753E2671DE9C005491E6 /* Typings.swift in Sources */,
|
||||
|
@ -657,6 +706,7 @@
|
|||
535870632669D21600D05A09 /* JellyfinPlayer_tvOSApp.swift in Sources */,
|
||||
53ABFDE4267974EF00886593 /* LibraryListViewModel.swift in Sources */,
|
||||
5364F456266CA0DC0026ECBA /* APIExtensions.swift in Sources */,
|
||||
531690FA267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift in Sources */,
|
||||
535870A32669D89F00D05A09 /* Model.xcdatamodeld in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -688,6 +738,7 @@
|
|||
5377CC01263B596B003A4E83 /* Model.xcdatamodeld in Sources */,
|
||||
53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */,
|
||||
6267B3D626710B8900A7371D /* CollectionExtensions.swift in Sources */,
|
||||
531690FD267AEDC5005D8AB9 /* HandleAPIRequestCompletion.swift in Sources */,
|
||||
53A089D0264DA9DA00D57806 /* MovieItemView.swift in Sources */,
|
||||
625CB56A2678B71200530A6E /* SplashViewModel.swift in Sources */,
|
||||
53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */,
|
||||
|
@ -697,12 +748,14 @@
|
|||
531AC8BF26750DE20091C7EB /* ImageView.swift in Sources */,
|
||||
62EC352F267666A5000E9F2D /* SessionManager.swift in Sources */,
|
||||
535870AD2669D8DD00D05A09 /* Typings.swift in Sources */,
|
||||
531690EF267ABF72005D8AB9 /* NextUpView.swift in Sources */,
|
||||
62EC352C26766675000E9F2D /* ServerEnvironment.swift in Sources */,
|
||||
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 */,
|
||||
531690EC267ABF46005D8AB9 /* ContinueWatchingView.swift in Sources */,
|
||||
5338F74E263B61370014BF09 /* ConnectToServerView.swift in Sources */,
|
||||
53AD124D267029D60094A276 /* SeriesItemView.swift in Sources */,
|
||||
5377CBF5263B596A003A4E83 /* JellyfinPlayerApp.swift in Sources */,
|
||||
|
@ -726,6 +779,7 @@
|
|||
628B95272670CABD0091AF3B /* NextUpWidget.swift in Sources */,
|
||||
628B95382670CDAB0091AF3B /* Model.xcdatamodeld in Sources */,
|
||||
62EC353226766849000E9F2D /* SessionManager.swift in Sources */,
|
||||
531690FF267AEDC5005D8AB9 /* HandleAPIRequestCompletion.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/* JellyfinPlayer/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 Combine
|
||||
import JellyfinAPI
|
||||
|
||||
func HandleAPIRequestCompletion(completion: Subscribers.Completion<Error>) {
|
||||
switch completion {
|
||||
case .finished:
|
||||
break
|
||||
case .failure(let error):
|
||||
if let err = error as? ErrorResponse {
|
||||
switch err {
|
||||
case .error(401, _, _, _):
|
||||
ServerEnvironment.current.errorMessage = "User unauthorized."
|
||||
ServerEnvironment.current.hasErrorMessage = true
|
||||
SessionManager.current.logout()
|
||||
case .error:
|
||||
ServerEnvironment.current.errorMessage = err.localizedDescription
|
||||
ServerEnvironment.current.hasErrorMessage = true
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
|
@ -1,17 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="18154" systemVersion="20E241" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19141.11" systemVersion="21A5248p" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
|
||||
<entity name="Server" representedClassName="Server" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="baseURI" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="name" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="server_id" attributeType="String" defaultValueString=""/>
|
||||
</entity>
|
||||
<entity name="SignedInUser" representedClassName="SignedInUser" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="appletv_id" optional="YES" attributeType="String"/>
|
||||
<attribute name="device_uuid" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="user_id" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="username" attributeType="String" defaultValueString=""/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="Server" positionX="-63" positionY="-9" width="128" height="74"/>
|
||||
<element name="SignedInUser" positionX="-63" positionY="9" width="128" height="74"/>
|
||||
<element name="SignedInUser" positionX="-63" positionY="9" width="128" height="89"/>
|
||||
</elements>
|
||||
</model>
|
Loading…
Reference in New Issue