luminate-old/Sources/Luminate/Luminate.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")
}
}
}