Add better views and flow for no servers/users
This commit is contained in:
parent
03edfe7e7c
commit
a5e6cdf998
|
@ -11,10 +11,10 @@ import SwiftUI
|
|||
import MessageUI
|
||||
|
||||
class EmailHelper: NSObject, MFMailComposeViewControllerDelegate {
|
||||
|
||||
public static let shared = EmailHelper()
|
||||
override private init() {
|
||||
//
|
||||
}
|
||||
|
||||
override private init() { }
|
||||
|
||||
func sendLogs(logURL: URL) {
|
||||
if !MFMailComposeViewController.canSendMail() {
|
||||
|
|
|
@ -148,31 +148,20 @@ struct JellyfinPlayerApp: App {
|
|||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
MainCoordinator().view()
|
||||
EmptyView()
|
||||
.onAppear {
|
||||
setupAppearance()
|
||||
}
|
||||
.withHostingWindow { window in
|
||||
window?.rootViewController = PreferenceUIHostingController(wrappedView: MainCoordinator().view())
|
||||
}
|
||||
.onShake {
|
||||
EmailHelper.shared.sendLogs(logURL: LogManager.shared.logFileURL())
|
||||
}
|
||||
.onOpenURL { url in
|
||||
AppURLHandler.shared.processDeepLink(url: url)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// WindowGroup {
|
||||
// EmptyView()
|
||||
// .environment(\.managedObjectContext, persistenceController.container.viewContext)
|
||||
// .onAppear(perform: {
|
||||
// setupAppearance()
|
||||
// })
|
||||
// .withHostingWindow { window in
|
||||
// window?
|
||||
// .rootViewController = PreferenceUIHostingController(wrappedView: MainCoordinator().view()
|
||||
// .environment(\.managedObjectContext, persistenceController.container.viewContext))
|
||||
// }
|
||||
// .onShake {
|
||||
// EmailHelper.shared.sendLogs(logURL: LogManager.shared.logFileURL())
|
||||
// }
|
||||
// .onOpenURL { url in
|
||||
// AppURLHandler.shared.processDeepLink(url: url)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private func setupAppearance() {
|
||||
|
|
|
@ -15,47 +15,120 @@ struct ServerListView: View {
|
|||
@EnvironmentObject var serverListRouter: ServerListCoordinator.Router
|
||||
@ObservedObject var viewModel: ServerListViewModel
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
ForEach(viewModel.servers, id: \.id) { server in
|
||||
Button {
|
||||
serverListRouter.route(to: \.userList, server)
|
||||
} label: {
|
||||
Text(server.name)
|
||||
@ViewBuilder
|
||||
private var listView: some View {
|
||||
ScrollView {
|
||||
VStack {
|
||||
ForEach(viewModel.servers, id: \.id) { server in
|
||||
Button {
|
||||
serverListRouter.route(to: \.userList, server)
|
||||
} label: {
|
||||
ZStack(alignment: Alignment.leading) {
|
||||
Rectangle()
|
||||
.foregroundColor(Color(UIColor.secondarySystemFill))
|
||||
.frame(height: 100)
|
||||
.cornerRadius(10)
|
||||
|
||||
HStack {
|
||||
Image(systemName: "server.rack")
|
||||
.font(.system(size: 36))
|
||||
.foregroundColor(.primary)
|
||||
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
Text(server.name)
|
||||
.font(.title2)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Text(server.uri)
|
||||
.font(.footnote)
|
||||
.disabled(true)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Text(viewModel.userTextFor(server: server))
|
||||
.font(.footnote)
|
||||
.foregroundColor(.primary)
|
||||
}
|
||||
}.padding([.leading])
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Servers")
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .navigation) {
|
||||
HStack {
|
||||
Button {
|
||||
serverListRouter.route(to: \.connectToServer)
|
||||
} label: {
|
||||
Text("Connect")
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var noServerView: some View {
|
||||
VStack {
|
||||
Text("Connect to a Jellyfin server to get started.")
|
||||
.frame(minWidth: 50, maxWidth: 240)
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
Button {
|
||||
serverListRouter.route(to: \.connectToServer)
|
||||
} label: {
|
||||
ZStack {
|
||||
Rectangle()
|
||||
.foregroundColor(Color.jellyfinPurple)
|
||||
.frame(maxWidth: 500, maxHeight: 50)
|
||||
.frame(height: 50)
|
||||
.cornerRadius(10)
|
||||
.padding([.leading, .trailing], 30)
|
||||
.padding([.top, .bottom], 20)
|
||||
|
||||
Button {
|
||||
SwiftfinStore.dataStack.perform(asynchronous: { transaction in
|
||||
try! transaction.deleteAll(From<SwiftfinStore.Models.StoredServer>())
|
||||
try! transaction.deleteAll(From<SwiftfinStore.Models.StoredUser>())
|
||||
try! transaction.deleteAll(From<SwiftfinStore.Models.StoredAccessToken>())
|
||||
}) { _ in
|
||||
SwiftfinStore.Defaults.suite[.lastServerUserID] = nil
|
||||
viewModel.fetchServers()
|
||||
}
|
||||
} label: {
|
||||
Text("Purge")
|
||||
}
|
||||
Text("Connect")
|
||||
.foregroundColor(Color.white)
|
||||
.bold()
|
||||
}
|
||||
}
|
||||
// ToolbarItem(placement: .navigationBarTrailing) {
|
||||
// Button {
|
||||
// serverListRouter.route(to: \.connectToServer)
|
||||
// } label: {
|
||||
// Text("Connect")
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var innerBody: some View {
|
||||
if viewModel.servers.isEmpty {
|
||||
noServerView
|
||||
.offset(y: -50)
|
||||
} else {
|
||||
listView
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var toolbarContent: some View {
|
||||
if viewModel.servers.isEmpty {
|
||||
EmptyView()
|
||||
} else {
|
||||
HStack {
|
||||
Button {
|
||||
SwiftfinStore.dataStack.perform(asynchronous: { transaction in
|
||||
try! transaction.deleteAll(From<SwiftfinStore.Models.StoredServer>())
|
||||
try! transaction.deleteAll(From<SwiftfinStore.Models.StoredUser>())
|
||||
try! transaction.deleteAll(From<SwiftfinStore.Models.StoredAccessToken>())
|
||||
}) { _ in
|
||||
SwiftfinStore.Defaults.suite[.lastServerUserID] = nil
|
||||
viewModel.fetchServers()
|
||||
}
|
||||
} label: {
|
||||
Text("Purge")
|
||||
}
|
||||
|
||||
Button {
|
||||
serverListRouter.route(to: \.connectToServer)
|
||||
} label: {
|
||||
Image(systemName: "plus.circle.fill")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
innerBody
|
||||
.navigationTitle("Servers")
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
||||
toolbarContent
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
viewModel.fetchServers()
|
||||
|
|
|
@ -14,34 +14,102 @@ struct UserListView: View {
|
|||
@EnvironmentObject var userListRouter: UserListCoordinator.Router
|
||||
@ObservedObject var viewModel: UserListViewModel
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
ForEach(viewModel.users, id: \.id) { user in
|
||||
Button {
|
||||
viewModel.login(user: user)
|
||||
} label: {
|
||||
HStack {
|
||||
Text(user.username)
|
||||
Spacer()
|
||||
if viewModel.isLoading {
|
||||
ProgressView()
|
||||
@ViewBuilder
|
||||
private var listView: some View {
|
||||
ScrollView {
|
||||
VStack {
|
||||
ForEach(viewModel.users, id: \.id) { user in
|
||||
Button {
|
||||
viewModel.login(user: user)
|
||||
} label: {
|
||||
ZStack(alignment: Alignment.leading) {
|
||||
Rectangle()
|
||||
.foregroundColor(Color(UIColor.secondarySystemFill))
|
||||
.frame(height: 70)
|
||||
.cornerRadius(10)
|
||||
|
||||
HStack {
|
||||
Image(systemName: "person.crop.circle.fill")
|
||||
.font(.system(size: 46))
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Text(user.username)
|
||||
.font(.title2)
|
||||
|
||||
Spacer()
|
||||
|
||||
if viewModel.isLoading {
|
||||
ProgressView()
|
||||
}
|
||||
}.padding(.leading)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Users")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigation) {
|
||||
HStack {
|
||||
Button {
|
||||
userListRouter.route(to: \.userLogin, viewModel.server)
|
||||
} label: {
|
||||
Text("Connect")
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var noUserView: some View {
|
||||
VStack {
|
||||
Text("Login to a user to get started.")
|
||||
.frame(minWidth: 50, maxWidth: 240)
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
Button {
|
||||
userListRouter.route(to: \.userLogin, viewModel.server)
|
||||
} label: {
|
||||
ZStack {
|
||||
Rectangle()
|
||||
.foregroundColor(Color.jellyfinPurple)
|
||||
.frame(maxWidth: 500, maxHeight: 50)
|
||||
.frame(height: 50)
|
||||
.cornerRadius(10)
|
||||
.padding([.leading, .trailing], 30)
|
||||
.padding([.top, .bottom], 20)
|
||||
|
||||
Text("Login")
|
||||
.foregroundColor(Color.white)
|
||||
.bold()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var innerBody: some View {
|
||||
if viewModel.users.isEmpty {
|
||||
noUserView
|
||||
.offset(y: -50)
|
||||
} else {
|
||||
listView
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var toolbarContent: some View {
|
||||
if viewModel.users.isEmpty {
|
||||
EmptyView()
|
||||
} else {
|
||||
HStack {
|
||||
Button {
|
||||
userListRouter.route(to: \.userLogin, viewModel.server)
|
||||
} label: {
|
||||
Image(systemName: "person.crop.circle.fill.badge.plus")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
innerBody
|
||||
.navigationTitle(viewModel.server.name)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
toolbarContent
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
viewModel.fetchUsers()
|
||||
}
|
||||
|
|
|
@ -17,4 +17,12 @@ class ServerListViewModel: ObservableObject {
|
|||
func fetchServers() {
|
||||
self.servers = SessionManager.main.fetchServers()
|
||||
}
|
||||
|
||||
func userTextFor(server: SwiftfinStore.State.Server) -> String {
|
||||
if server.userIDs.count == 1 {
|
||||
return "1 user"
|
||||
} else {
|
||||
return "\(server.userIDs.count) users"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue