diff options
Diffstat (limited to 'Jel')
-rw-r--r-- | Jel/ContentView.swift | 19 | ||||
-rw-r--r-- | Jel/Controllers/AuthStateController.swift | 38 | ||||
-rw-r--r-- | Jel/Controllers/DatamodelController.swift | 25 | ||||
-rw-r--r-- | Jel/Controllers/JellyfinClientController.swift | 30 | ||||
-rw-r--r-- | Jel/JelApp.swift | 13 | ||||
-rw-r--r-- | Jel/Model.xcdatamodeld/Model.xcdatamodel/contents | 2 | ||||
-rw-r--r-- | Jel/Views/AddServerView.swift | 109 |
7 files changed, 227 insertions, 9 deletions
diff --git a/Jel/ContentView.swift b/Jel/ContentView.swift index 69609c1..2c388be 100644 --- a/Jel/ContentView.swift +++ b/Jel/ContentView.swift @@ -8,17 +8,24 @@ import SwiftUI struct ContentView: View { - var body: some View { + @ObservedObject var authState: AuthStateController + + var body: some View { VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundStyle(.tint) - Text("Hello, world!") + if !authState.loggedIn { + AddServerView(authState: authState) + } else { + Text("Logged in") + Button("Log out") { + authState.loggedIn = false + authState.save() + } + } } .padding() } } #Preview { - ContentView() + ContentView(authState: AuthStateController()) } diff --git a/Jel/Controllers/AuthStateController.swift b/Jel/Controllers/AuthStateController.swift new file mode 100644 index 0000000..1629556 --- /dev/null +++ b/Jel/Controllers/AuthStateController.swift @@ -0,0 +1,38 @@ +// +// AuthStateController.swift +// Jel +// +// Created by zerocool on 12/11/23. +// + +import Foundation + +class AuthStateController: ObservableObject { + @Published var loggedIn: Bool + @Published var serverUrl: URL? + @Published var authToken: String? + + private let defaults = UserDefaults.standard + + init(loggedIn: Bool = false, serverUrl: URL? = nil, authToken: String? = nil) { + self.loggedIn = loggedIn + self.serverUrl = serverUrl + self.authToken = authToken + } + + func load() { + self.loggedIn = defaults.bool(forKey: "AuthState_loggedIn") + if let oldServerUrl = defaults.url(forKey: "AuthState_serverUrl") { + self.serverUrl = oldServerUrl + } + if let oldAuthToken = defaults.string(forKey: "AuthState_authToken") { + self.authToken = oldAuthToken + } + } + + func save() { + defaults.set(self.loggedIn, forKey: "AuthState_loggedIn") + defaults.set(self.serverUrl, forKey: "AuthState_serverUrl") + defaults.set(self.authToken, forKey: "AuthState_authToken") + } +} diff --git a/Jel/Controllers/DatamodelController.swift b/Jel/Controllers/DatamodelController.swift new file mode 100644 index 0000000..4beb173 --- /dev/null +++ b/Jel/Controllers/DatamodelController.swift @@ -0,0 +1,25 @@ +// +// DatamodelController.swift +// Jel +// +// Created by zerocool on 12/11/23. +// + +import CoreData + +struct DatamodelController { + static let shared = DatamodelController() + + let container: NSPersistentContainer + + init() { + container = NSPersistentContainer(name: "Model") + + container.loadPersistentStores(completionHandler: {(storeDescription, error) in + if let error = error as NSError? { + fatalError("Unresolved error \(error), \(error.userInfo)") + } + }) + container.viewContext.automaticallyMergesChangesFromParent = true + } +} diff --git a/Jel/Controllers/JellyfinClientController.swift b/Jel/Controllers/JellyfinClientController.swift new file mode 100644 index 0000000..343efe1 --- /dev/null +++ b/Jel/Controllers/JellyfinClientController.swift @@ -0,0 +1,30 @@ +// +// JellyfinClientController.swift +// Jel +// +// Created by zerocool on 12/12/23. +// + +import Foundation +import Get +import JellyfinKit + +class JellyfinClientController { + let api: APIClient + + init(serverUrl: URL) { + self.api = APIClient( + baseURL: serverUrl + ) + } + + func isJellyfinServer() async -> Bool { + let request = Paths.getPublicUsers + do { + try await api.send(request) + } catch { + return false + } + return true + } +} diff --git a/Jel/JelApp.swift b/Jel/JelApp.swift index ddff76b..d70e444 100644 --- a/Jel/JelApp.swift +++ b/Jel/JelApp.swift @@ -9,9 +9,16 @@ import SwiftUI @main struct JelApp: App { - var body: some Scene { - WindowGroup { - ContentView() + let datamodelController = DatamodelController.shared + let authStateController = AuthStateController() + var body: some Scene { + WindowGroup { + ContentView(authState: authStateController) + .environment(\.managedObjectContext, + datamodelController.container.viewContext) + .task { + authStateController.load() } } + } } diff --git a/Jel/Model.xcdatamodeld/Model.xcdatamodel/contents b/Jel/Model.xcdatamodeld/Model.xcdatamodel/contents new file mode 100644 index 0000000..38ef213 --- /dev/null +++ b/Jel/Model.xcdatamodeld/Model.xcdatamodel/contents @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22225" systemVersion="23A344" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithSwiftData="YES" userDefinedModelVersionIdentifier=""/>
\ No newline at end of file diff --git a/Jel/Views/AddServerView.swift b/Jel/Views/AddServerView.swift new file mode 100644 index 0000000..beab5e7 --- /dev/null +++ b/Jel/Views/AddServerView.swift @@ -0,0 +1,109 @@ +// +// AddServerView.swift +// Jel +// +// Created by zerocool on 12/11/23. +// + +import SwiftUI + +struct AddServerView: View { + @ObservedObject var authState: AuthStateController + + @State var serverUrlString: String = "" + @State var urlHasError: Bool = false + @State var currentErrorMessage: String = "" + @State var loading: Bool = false + + @FocusState var serverUrlIsFocused: Bool + + var body: some View { + VStack { + Text("Connect to a server") + .font(.title) + HStack { + + TextField(text: $serverUrlString) { + Text("http://jellyfin.example.com") + .foregroundStyle(.placeholder) + } + .keyboardType(.URL) + .textContentType(.URL) + .textFieldStyle(.roundedBorder) + .textInputAutocapitalization(.never) + .autocorrectionDisabled() + .focused($serverUrlIsFocused) + .onChange(of: serverUrlIsFocused) { + if serverUrlIsFocused { + urlHasError = false + } + } + .onSubmit { + Task { + await checkServerUrl() + } + } + + + if !loading { + Button(action: { + Task { + await checkServerUrl() + } + }) { + Label("Connect", systemImage: "arrow.right") + .labelStyle(.iconOnly) + } + .buttonStyle(.bordered) + .disabled(urlHasError) + } else { + ProgressView() + .progressViewStyle(.circular) + .padding() + } + } + .padding() + + if urlHasError { + Text(currentErrorMessage) + .font(.callout) + .foregroundStyle(.red) + } + } + } + + func checkServerUrl() async { + loading = true + serverUrlIsFocused = false + if isValidUrl(data: serverUrlString) { + let url = URL(string: serverUrlString)! + if await JellyfinClientController(serverUrl: url).isJellyfinServer() { + authState.serverUrl = url + urlHasError = false + } else { + urlHasError = true + currentErrorMessage = "Server not responding" + } + + } else { + urlHasError = true + currentErrorMessage = "Invalid url" + } + + loading = false + } + + func isValidUrl(data: String) -> Bool { + if let url = URL(string: data) { + if UIApplication.shared.canOpenURL(url) { + return true + } + } + return false + } + +} + +#Preview { + AddServerView(authState: AuthStateController()) +} |