154 lines
5 KiB
Swift
154 lines
5 KiB
Swift
//
|
|
// Luminate.swift
|
|
//
|
|
// Copyright 2026 Brendan Szymanski <hello@bscubed.dev>
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
|
|
import Adwaita
|
|
import Foundation
|
|
import Logging
|
|
import LuminateCore
|
|
import LuminateDI
|
|
import LuminatePlayer
|
|
import LuminateUI
|
|
|
|
@main
|
|
struct Luminate: App {
|
|
|
|
let app = AdwaitaApp(id: "dev.bscubed.Luminate")
|
|
@State private var client: JellyfinClient?
|
|
@State private var userId = ""
|
|
@State private var isLaunchLoading = true
|
|
|
|
init() {
|
|
LoggingSystem.bootstrap { label in
|
|
var handler = StreamLogHandler.standardOutput(label: label)
|
|
#if DEBUG
|
|
handler.logLevel = .debug
|
|
#else
|
|
handler.logLevel = .info
|
|
#endif
|
|
return handler
|
|
}
|
|
ObservationRegistrar.onChange = { StateManager.updateViews() }
|
|
if let store = try? SQLiteStore(dbURL: SQLiteStore.defaultDatabaseURL()) {
|
|
DIContainer.shared.register(\.persistence, value: store)
|
|
}
|
|
DIContainer.shared.register(\.imageService, value: ImageService())
|
|
DIContainer.shared.register(\.viewUpdateScheduler, value: ViewUpdateScheduler())
|
|
DIContainer.shared.register(\.logger, value: Logger(label: "dev.bscubed.Luminate"))
|
|
}
|
|
|
|
var scene: Scene {
|
|
Window(id: "main") { window in
|
|
if isLaunchLoading {
|
|
VStack {
|
|
Spinner()
|
|
}
|
|
.onAppear {
|
|
loadSavedSession()
|
|
}
|
|
} else if let client {
|
|
ContentView(
|
|
client: client,
|
|
userId: userId
|
|
)
|
|
} else {
|
|
ServerSetupView { client, id in
|
|
DIContainer.shared.register(\.client, value: client)
|
|
DIContainer.shared.register(\.userId, value: id)
|
|
|
|
self.client = client
|
|
self.userId = id
|
|
}
|
|
}
|
|
}
|
|
.defaultSize(width: 1280, height: 800)
|
|
.quitShortcut()
|
|
.closeShortcut()
|
|
.keyboardShortcut("f".ctrl()) { _ in
|
|
}
|
|
.keyboardShortcut("r".ctrl()) { _ in
|
|
}
|
|
#if DEBUG
|
|
.devel()
|
|
#endif
|
|
}
|
|
|
|
private func loadSavedSession() {
|
|
Task {
|
|
do {
|
|
defer { isLaunchLoading = false }
|
|
|
|
let store = try SQLiteStore(dbURL: SQLiteStore.defaultDatabaseURL())
|
|
let auth = try await store.loadAuth()
|
|
guard let serverURL = URL(string: auth.serverURL) else { return }
|
|
|
|
let client = JellyfinClient(
|
|
serverURL: serverURL, token: auth.token, userId: auth.userId)
|
|
let isValid = await client.validateToken()
|
|
guard isValid else { return }
|
|
|
|
DIContainer.shared.register(\.client, value: client)
|
|
DIContainer.shared.register(\.userId, value: auth.userId)
|
|
|
|
self.client = client
|
|
self.userId = auth.userId
|
|
} catch {
|
|
isLaunchLoading = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ContentView: View {
|
|
|
|
var client: JellyfinClient
|
|
var userId: String
|
|
@State var stack: NavigationStack<Page> = .init()
|
|
var view: Body {
|
|
NavigationView($stack, "Luminate") { page in
|
|
switch page {
|
|
case .items(let title, let items, let type):
|
|
ItemPage(title: title, items: items, type: type, navigation: $stack)
|
|
.topToolbar {
|
|
ToolbarView()
|
|
}
|
|
.navigationTitle(page.description)
|
|
case .item(let item, let type):
|
|
ItemPage(item: item, type: type, navigation: $stack)
|
|
.topToolbar {
|
|
ToolbarView()
|
|
}
|
|
.navigationTitle(page.description)
|
|
case .movieDetail(let item):
|
|
MovieDetailView(for: item)
|
|
.topToolbar {
|
|
ToolbarView()
|
|
}
|
|
.navigationTitle(page.description)
|
|
}
|
|
} initialView: {
|
|
HomeView(navigation: $stack)
|
|
.topToolbar {
|
|
ToolbarView()
|
|
}
|
|
.navigationTitle("Luminate")
|
|
}
|
|
}
|
|
}
|