Migrate Swift Language Mode From V5 To V6
This commit is contained in:
parent
7c0a0d481f
commit
45cbfb418b
18 changed files with 103 additions and 103 deletions
|
|
@ -85,5 +85,5 @@ let package = Package(
|
||||||
plugins: [.plugin(name: "GenerateLocalized", package: "localized")]
|
plugins: [.plugin(name: "GenerateLocalized", package: "localized")]
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
swiftLanguageModes: [.v5]
|
swiftLanguageModes: [.v6]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
// Created by Brendan Szymanski on 6/14/26.
|
// Created by Brendan Szymanski on 6/14/26.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Adwaita
|
@preconcurrency import Adwaita
|
||||||
import Foundation
|
import Foundation
|
||||||
import LuminateCore
|
import LuminateCore
|
||||||
import LuminateDI
|
import LuminateDI
|
||||||
|
|
@ -15,15 +15,20 @@ import LuminateUI
|
||||||
@main
|
@main
|
||||||
struct Luminate: App {
|
struct Luminate: App {
|
||||||
|
|
||||||
let app = AdwaitaApp(id: "dev.bscubed.Luminate")
|
nonisolated(unsafe) let app = AdwaitaApp(id: "dev.bscubed.Luminate")
|
||||||
@State private var client: JellyfinClient?
|
@State private nonisolated(unsafe) var client: JellyfinClient?
|
||||||
@State private var userId = ""
|
@State private nonisolated(unsafe) var userId = ""
|
||||||
@State private var isLaunchLoading = true
|
@State private nonisolated(unsafe) var isLaunchLoading = true
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
ObservationRegistrar.onChange = { StateManager.updateViews() }
|
Task { @MainActor in
|
||||||
if let store = try? SQLiteStore(dbURL: SQLiteStore.defaultDatabaseURL()) {
|
ObservationRegistrar.onChange = { StateManager.updateViews() }
|
||||||
DIContainer.shared.register(\.persistence, value: store)
|
}
|
||||||
|
let dbURL = SQLiteStore.defaultDatabaseURL()
|
||||||
|
Task { [dbURL] in
|
||||||
|
if let store = try? await SQLiteStore(dbURL: dbURL) {
|
||||||
|
DIContainer.shared.register(\.persistence, value: store)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DIContainer.shared.register(\.imageService, value: ImageService())
|
DIContainer.shared.register(\.imageService, value: ImageService())
|
||||||
DIContainer.shared.register(\.pageAnimationTracker, value: PageAnimationTracker())
|
DIContainer.shared.register(\.pageAnimationTracker, value: PageAnimationTracker())
|
||||||
|
|
@ -63,11 +68,11 @@ struct Luminate: App {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadSavedSession() {
|
private func loadSavedSession() {
|
||||||
Task {
|
Task { [self] in
|
||||||
do {
|
do {
|
||||||
defer { isLaunchLoading = false }
|
defer { isLaunchLoading = false }
|
||||||
|
|
||||||
let store = try SQLiteStore(dbURL: SQLiteStore.defaultDatabaseURL())
|
let store = try await SQLiteStore(dbURL: SQLiteStore.defaultDatabaseURL())
|
||||||
let auth = try await store.loadAuth()
|
let auth = try await store.loadAuth()
|
||||||
guard let serverURL = URL(string: auth.serverURL) else { return }
|
guard let serverURL = URL(string: auth.serverURL) else { return }
|
||||||
|
|
||||||
|
|
@ -92,7 +97,7 @@ struct ContentView: View {
|
||||||
|
|
||||||
var client: JellyfinClient
|
var client: JellyfinClient
|
||||||
var userId: String
|
var userId: String
|
||||||
@State var stack: NavigationStack<Page> = .init()
|
@State nonisolated(unsafe) var stack: NavigationStack<Page> = .init()
|
||||||
@Injected(\.pageAnimationTracker) var pageAnimationTracker
|
@Injected(\.pageAnimationTracker) var pageAnimationTracker
|
||||||
|
|
||||||
var view: Body {
|
var view: Body {
|
||||||
|
|
|
||||||
|
|
@ -5,26 +5,26 @@
|
||||||
// Created by Brendan Szymanski on 6/5/26.
|
// Created by Brendan Szymanski on 6/5/26.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Adwaita
|
@preconcurrency import Adwaita
|
||||||
import LuminateCore
|
import LuminateCore
|
||||||
import LuminateDI
|
import LuminateDI
|
||||||
import LuminateUI
|
import LuminateUI
|
||||||
|
|
||||||
public struct HomeView: View {
|
public struct HomeView: View {
|
||||||
|
|
||||||
nonisolated public init(navigation: Binding<NavigationStack<Page>>) {
|
public init(navigation: Binding<NavigationStack<Page>>) {
|
||||||
_navigation = navigation
|
_navigation = navigation
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injected(\.client) var client
|
@Injected(\.client) var client
|
||||||
@Injected(\.userId) var userId
|
@Injected(\.userId) var userId
|
||||||
@Binding var navigation: NavigationStack<Page>
|
@Binding nonisolated(unsafe) var navigation: NavigationStack<Page>
|
||||||
@State private var resumeItems: [Components.Schemas.BaseItemDto] = []
|
@State private nonisolated(unsafe) var resumeItems: [Components.Schemas.BaseItemDto] = []
|
||||||
@State private var nextUpItems: [Components.Schemas.BaseItemDto] = []
|
@State private nonisolated(unsafe) var nextUpItems: [Components.Schemas.BaseItemDto] = []
|
||||||
@State private var latestItems: [Components.Schemas.BaseItemDto] = []
|
@State private nonisolated(unsafe) var latestItems: [Components.Schemas.BaseItemDto] = []
|
||||||
@State private var libraries: [Components.Schemas.BaseItemDto] = []
|
@State private nonisolated(unsafe) var libraries: [Components.Schemas.BaseItemDto] = []
|
||||||
@State private var isLoading = true
|
@State private nonisolated(unsafe) var isLoading = true
|
||||||
@State private var isLoadingData = false
|
@State private nonisolated(unsafe) var isLoadingData = false
|
||||||
|
|
||||||
public var view: Body {
|
public var view: Body {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
|
|
@ -96,7 +96,7 @@ public struct HomeView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadHomeData() {
|
private func loadHomeData() {
|
||||||
Task {
|
Task { [self] in
|
||||||
async let resume = client.getItems(
|
async let resume = client.getItems(
|
||||||
userId: userId,
|
userId: userId,
|
||||||
filters: [.isResumable],
|
filters: [.isResumable],
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import LuminateUI
|
||||||
|
|
||||||
public struct LibraryPage: View {
|
public struct LibraryPage: View {
|
||||||
|
|
||||||
nonisolated public init(
|
public init(
|
||||||
title: String, items: [Components.Schemas.BaseItemDto],
|
title: String, items: [Components.Schemas.BaseItemDto],
|
||||||
navigation: Binding<NavigationStack<Page>>
|
navigation: Binding<NavigationStack<Page>>
|
||||||
) {
|
) {
|
||||||
|
|
@ -22,7 +22,7 @@ public struct LibraryPage: View {
|
||||||
|
|
||||||
private var items: [Components.Schemas.BaseItemDto]
|
private var items: [Components.Schemas.BaseItemDto]
|
||||||
private var title: String
|
private var title: String
|
||||||
@Binding var navigation: NavigationStack<Page>
|
@Binding nonisolated(unsafe) var navigation: NavigationStack<Page>
|
||||||
|
|
||||||
public var view: Body {
|
public var view: Body {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,21 @@
|
||||||
// Created by Brendan Szymanski on 6/5/26.
|
// Created by Brendan Szymanski on 6/5/26.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Adwaita
|
@preconcurrency import Adwaita
|
||||||
import Foundation
|
import Foundation
|
||||||
import LuminateCore
|
import LuminateCore
|
||||||
import LuminateDI
|
import LuminateDI
|
||||||
|
|
||||||
public struct ServerSetupView: View {
|
public struct ServerSetupView: View {
|
||||||
|
|
||||||
@State private var serverURL = ""
|
@State private nonisolated(unsafe) var serverURL = ""
|
||||||
@State private var username = ""
|
@State private nonisolated(unsafe) var username = ""
|
||||||
@State private var password = ""
|
@State private nonisolated(unsafe) var password = ""
|
||||||
@State private var isLoading = false
|
@State private nonisolated(unsafe) var isLoading = false
|
||||||
@State private var error: String?
|
@State private nonisolated(unsafe) var error: String?
|
||||||
public var onLogin: (JellyfinClient, String) -> Void
|
public var onLogin: @Sendable (JellyfinClient, String) -> Void
|
||||||
|
|
||||||
public init(onLogin: @escaping (JellyfinClient, String) -> Void) {
|
public init(onLogin: @escaping @Sendable (JellyfinClient, String) -> Void) {
|
||||||
self.onLogin = onLogin
|
self.onLogin = onLogin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,7 +65,7 @@ public struct ServerSetupView: View {
|
||||||
}
|
}
|
||||||
isLoading = true
|
isLoading = true
|
||||||
error = nil
|
error = nil
|
||||||
Task {
|
Task { [self] in
|
||||||
do {
|
do {
|
||||||
let client = JellyfinClient(serverURL: url)
|
let client = JellyfinClient(serverURL: url)
|
||||||
let result = try await client.authenticate(username: username, password: password)
|
let result = try await client.authenticate(username: username, password: password)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import Foundation
|
||||||
/// ```swift
|
/// ```swift
|
||||||
/// ObservationRegistrar.onChange = { StateManager.updateViews() }
|
/// ObservationRegistrar.onChange = { StateManager.updateViews() }
|
||||||
/// ```
|
/// ```
|
||||||
|
@MainActor
|
||||||
public class ObservationRegistrar {
|
public class ObservationRegistrar {
|
||||||
|
|
||||||
/// Global callback invoked when any tracked property changes.
|
/// Global callback invoked when any tracked property changes.
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ public actor SQLiteStore: PersistenceService {
|
||||||
|
|
||||||
private let db: Connection
|
private let db: Connection
|
||||||
|
|
||||||
public init(dbURL: URL) throws {
|
public init(dbURL: URL) async throws {
|
||||||
let directory = dbURL.deletingLastPathComponent()
|
let directory = dbURL.deletingLastPathComponent()
|
||||||
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
|
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
|
||||||
db = try Connection(dbURL.path)
|
db = try Connection(dbURL.path)
|
||||||
|
|
@ -23,7 +23,7 @@ public actor SQLiteStore: PersistenceService {
|
||||||
try migrate()
|
try migrate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func migrate() throws {
|
private nonisolated func migrate() throws {
|
||||||
let version = db.userVersion
|
let version = db.userVersion
|
||||||
switch version {
|
switch version {
|
||||||
case 0:
|
case 0:
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ public struct Injected<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ObserverRef {
|
private final class ObserverRef: @unchecked Sendable {
|
||||||
let id: UUID
|
let id: UUID
|
||||||
init(id: UUID) { self.id = id }
|
init(id: UUID) { self.id = id }
|
||||||
deinit { DIContainer.shared.removeObserver(id) }
|
deinit { DIContainer.shared.removeObserver(id) }
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
// Created by Brendan Szymanski on 6/5/26.
|
// Created by Brendan Szymanski on 6/5/26.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Adwaita
|
@preconcurrency import Adwaita
|
||||||
import Foundation
|
import Foundation
|
||||||
import LuminateCore
|
import LuminateCore
|
||||||
|
|
||||||
|
|
@ -17,11 +17,11 @@ public struct PlayerView: View {
|
||||||
public var mediaSourceId: String
|
public var mediaSourceId: String
|
||||||
public var playSessionId: String
|
public var playSessionId: String
|
||||||
public var streamURL: URL
|
public var streamURL: URL
|
||||||
@State private var isPlaying = true
|
@State private nonisolated(unsafe) var isPlaying = true
|
||||||
@State private var position: Double = 0
|
@State private nonisolated(unsafe) var position: Double = 0
|
||||||
@State private var duration: Double = 0
|
@State private nonisolated(unsafe) var duration: Double = 0
|
||||||
@State private var showControls = true
|
@State private nonisolated(unsafe) var showControls = true
|
||||||
public var onClose: () -> Void
|
public var onClose: @Sendable () -> Void
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
item: Components.Schemas.BaseItemDto,
|
item: Components.Schemas.BaseItemDto,
|
||||||
|
|
@ -30,7 +30,7 @@ public struct PlayerView: View {
|
||||||
mediaSourceId: String,
|
mediaSourceId: String,
|
||||||
playSessionId: String,
|
playSessionId: String,
|
||||||
streamURL: URL,
|
streamURL: URL,
|
||||||
onClose: @escaping () -> Void
|
onClose: @escaping @Sendable () -> Void
|
||||||
) {
|
) {
|
||||||
self.item = item
|
self.item = item
|
||||||
self.client = client
|
self.client = client
|
||||||
|
|
@ -70,7 +70,7 @@ public struct PlayerView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startPlayback() {
|
private func startPlayback() {
|
||||||
Task {
|
Task { [self] in
|
||||||
try? await client.reportPlaybackStart(
|
try? await client.reportPlaybackStart(
|
||||||
info: .init(
|
info: .init(
|
||||||
itemId: item.id,
|
itemId: item.id,
|
||||||
|
|
@ -82,7 +82,7 @@ public struct PlayerView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func stopPlayback() {
|
private func stopPlayback() {
|
||||||
Task {
|
Task { [self] in
|
||||||
try? await client.reportPlaybackStopped(
|
try? await client.reportPlaybackStopped(
|
||||||
info: .init(
|
info: .init(
|
||||||
itemId: item.id,
|
itemId: item.id,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
// Created by Brendan Szymanski on 6/5/26.
|
// Created by Brendan Szymanski on 6/5/26.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Adwaita
|
@preconcurrency import Adwaita
|
||||||
import Foundation
|
import Foundation
|
||||||
import LuminateCore
|
import LuminateCore
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ struct EpisodeList: View {
|
||||||
var seasonId: String
|
var seasonId: String
|
||||||
var client: JellyfinClient
|
var client: JellyfinClient
|
||||||
var userId: String
|
var userId: String
|
||||||
@State private var episodes: [Components.Schemas.BaseItemDto] = []
|
@State private nonisolated(unsafe) var episodes: [Components.Schemas.BaseItemDto] = []
|
||||||
|
|
||||||
var view: Body {
|
var view: Body {
|
||||||
VStack {
|
VStack {
|
||||||
|
|
@ -32,13 +32,13 @@ struct EpisodeList: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadEpisodes() {
|
private func loadEpisodes() {
|
||||||
Task {
|
Task { [self] in
|
||||||
let result = try? await client.getEpisodes(
|
let result = try? await client.getEpisodes(
|
||||||
seriesId: seriesId,
|
seriesId: seriesId,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
seasonId: seasonId
|
seasonId: seasonId
|
||||||
)
|
)
|
||||||
await MainActor.run { episodes = result?.items ?? [] }
|
episodes = result?.items ?? []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -47,7 +47,7 @@ struct EpisodeRow: View {
|
||||||
|
|
||||||
var episode: Components.Schemas.BaseItemDto
|
var episode: Components.Schemas.BaseItemDto
|
||||||
var client: JellyfinClient
|
var client: JellyfinClient
|
||||||
@State private var imageData: Data?
|
@State private nonisolated(unsafe) var imageData: Data?
|
||||||
|
|
||||||
var view: Body {
|
var view: Body {
|
||||||
HStack {
|
HStack {
|
||||||
|
|
@ -89,7 +89,7 @@ struct EpisodeRow: View {
|
||||||
guard let tag = episode.primaryImageTag, let itemId = episode.id else {
|
guard let tag = episode.primaryImageTag, let itemId = episode.id else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Task {
|
Task { [self] in
|
||||||
guard
|
guard
|
||||||
let url = await client.imageURL(
|
let url = await client.imageURL(
|
||||||
itemId: itemId,
|
itemId: itemId,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
// Created by Brendan Szymanski on 6/5/26.
|
// Created by Brendan Szymanski on 6/5/26.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Adwaita
|
@preconcurrency import Adwaita
|
||||||
import Foundation
|
import Foundation
|
||||||
import LuminateCore
|
import LuminateCore
|
||||||
import LuminateDI
|
import LuminateDI
|
||||||
|
|
@ -15,7 +15,7 @@ struct HomePosterCell: View {
|
||||||
var item: Components.Schemas.BaseItemDto
|
var item: Components.Schemas.BaseItemDto
|
||||||
@Injected(\.client) var client
|
@Injected(\.client) var client
|
||||||
@Injected(\.pageAnimationTracker) var pageAnimationTracker
|
@Injected(\.pageAnimationTracker) var pageAnimationTracker
|
||||||
@State private var imageData: Data?
|
@State private nonisolated(unsafe) var imageData: Data?
|
||||||
|
|
||||||
var view: Body {
|
var view: Body {
|
||||||
VStack {
|
VStack {
|
||||||
|
|
@ -67,7 +67,7 @@ struct HomePosterCell: View {
|
||||||
guard let tag = item.primaryImageTag,
|
guard let tag = item.primaryImageTag,
|
||||||
let itemId = item.seriesId ?? item.id
|
let itemId = item.seriesId ?? item.id
|
||||||
else { return }
|
else { return }
|
||||||
Task {
|
Task { [self] in
|
||||||
guard
|
guard
|
||||||
let url = await client.imageURL(
|
let url = await client.imageURL(
|
||||||
itemId: itemId,
|
itemId: itemId,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
// Created by Brendan Szymanski on 6/5/26.
|
// Created by Brendan Szymanski on 6/5/26.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Adwaita
|
@preconcurrency import Adwaita
|
||||||
import LuminateCore
|
import LuminateCore
|
||||||
|
|
||||||
public struct ItemGrid: View {
|
public struct ItemGrid: View {
|
||||||
|
|
@ -15,8 +15,8 @@ public struct ItemGrid: View {
|
||||||
var parentId: String?
|
var parentId: String?
|
||||||
var includeItemTypes: [Components.Schemas.BaseItemKind]?
|
var includeItemTypes: [Components.Schemas.BaseItemKind]?
|
||||||
var title: String?
|
var title: String?
|
||||||
@State private var items: [Components.Schemas.BaseItemDto] = []
|
@State private nonisolated(unsafe) var items: [Components.Schemas.BaseItemDto] = []
|
||||||
@State private var isLoading = false
|
@State private nonisolated(unsafe) var isLoading = false
|
||||||
private let pageSize: Int32 = 50
|
private let pageSize: Int32 = 50
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
|
|
@ -58,7 +58,7 @@ public struct ItemGrid: View {
|
||||||
|
|
||||||
private func loadItems() {
|
private func loadItems() {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
Task {
|
Task { [self] in
|
||||||
do {
|
do {
|
||||||
let result = try await client.getItems(
|
let result = try await client.getItems(
|
||||||
userId: userId,
|
userId: userId,
|
||||||
|
|
@ -71,12 +71,10 @@ public struct ItemGrid: View {
|
||||||
limit: pageSize,
|
limit: pageSize,
|
||||||
recursive: true
|
recursive: true
|
||||||
)
|
)
|
||||||
await MainActor.run {
|
items = result.items ?? []
|
||||||
items = result.items ?? []
|
isLoading = false
|
||||||
isLoading = false
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
await MainActor.run { isLoading = false }
|
isLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import LuminateCore
|
||||||
public struct LibraryGrid: View {
|
public struct LibraryGrid: View {
|
||||||
|
|
||||||
public var libraries: [Components.Schemas.BaseItemDto]
|
public var libraries: [Components.Schemas.BaseItemDto]
|
||||||
@Binding public var navigation: NavigationStack<Page>
|
@Binding public nonisolated(unsafe) var navigation: NavigationStack<Page>
|
||||||
public var title: String?
|
public var title: String?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,14 @@ public struct MediaRow: View {
|
||||||
|
|
||||||
public var title: String
|
public var title: String
|
||||||
public var items: [Components.Schemas.BaseItemDto]
|
public var items: [Components.Schemas.BaseItemDto]
|
||||||
@Binding public var navigation: NavigationStack<Page>
|
@Binding public nonisolated(unsafe) var navigation: NavigationStack<Page>
|
||||||
public var onSeeAll: (() -> Void)?
|
public var onSeeAll: (@Sendable () -> Void)?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
title: String,
|
title: String,
|
||||||
items: [Components.Schemas.BaseItemDto],
|
items: [Components.Schemas.BaseItemDto],
|
||||||
navigation: Binding<NavigationStack<Page>>,
|
navigation: Binding<NavigationStack<Page>>,
|
||||||
onSeeAll: (() -> Void)? = nil
|
onSeeAll: (@Sendable () -> Void)? = nil
|
||||||
) {
|
) {
|
||||||
self.title = title
|
self.title = title
|
||||||
self.items = items
|
self.items = items
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
// Created by Brendan Szymanski on 6/5/26.
|
// Created by Brendan Szymanski on 6/5/26.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Adwaita
|
@preconcurrency import Adwaita
|
||||||
import Foundation
|
import Foundation
|
||||||
import LuminateCore
|
import LuminateCore
|
||||||
|
|
||||||
|
|
@ -14,10 +14,10 @@ struct MovieDetailView: View {
|
||||||
var item: Components.Schemas.BaseItemDto
|
var item: Components.Schemas.BaseItemDto
|
||||||
var client: JellyfinClient
|
var client: JellyfinClient
|
||||||
var userId: String
|
var userId: String
|
||||||
@State private var isFavorite: Bool
|
@State private nonisolated(unsafe) var isFavorite: Bool
|
||||||
@State private var isPlayed: Bool
|
@State private nonisolated(unsafe) var isPlayed: Bool
|
||||||
@State private var similarItems: [Components.Schemas.BaseItemDto] = []
|
@State private nonisolated(unsafe) var similarItems: [Components.Schemas.BaseItemDto] = []
|
||||||
@State private var backdropData: Data?
|
@State private nonisolated(unsafe) var backdropData: Data?
|
||||||
|
|
||||||
init(item: Components.Schemas.BaseItemDto, client: JellyfinClient, userId: String) {
|
init(item: Components.Schemas.BaseItemDto, client: JellyfinClient, userId: String) {
|
||||||
self.item = item
|
self.item = item
|
||||||
|
|
@ -115,7 +115,7 @@ struct MovieDetailView: View {
|
||||||
|
|
||||||
private func loadBackdrop() {
|
private func loadBackdrop() {
|
||||||
guard let tag = item.backdropImageTag, let itemId = item.id else { return }
|
guard let tag = item.backdropImageTag, let itemId = item.id else { return }
|
||||||
Task {
|
Task { [self] in
|
||||||
guard
|
guard
|
||||||
let url = await client.imageURL(
|
let url = await client.imageURL(
|
||||||
itemId: itemId, imageType: .backdrop, tag: tag, maxWidth: 1920
|
itemId: itemId, imageType: .backdrop, tag: tag, maxWidth: 1920
|
||||||
|
|
@ -127,7 +127,7 @@ struct MovieDetailView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadSimilar() {
|
private func loadSimilar() {
|
||||||
Task {
|
Task { [self] in
|
||||||
let result = try? await client.getItems(
|
let result = try? await client.getItems(
|
||||||
userId: userId,
|
userId: userId,
|
||||||
includeItemTypes: [.movie],
|
includeItemTypes: [.movie],
|
||||||
|
|
@ -136,29 +136,29 @@ struct MovieDetailView: View {
|
||||||
limit: 10,
|
limit: 10,
|
||||||
recursive: true
|
recursive: true
|
||||||
)
|
)
|
||||||
await MainActor.run { similarItems = result?.items ?? [] }
|
similarItems = result?.items ?? []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func toggleFavorite() {
|
private func toggleFavorite() {
|
||||||
Task {
|
Task { [self] in
|
||||||
if isFavorite {
|
if isFavorite {
|
||||||
try? await client.unmarkFavoriteItem(itemId: item.id ?? "", userId: userId)
|
try? await client.unmarkFavoriteItem(itemId: item.id ?? "", userId: userId)
|
||||||
} else {
|
} else {
|
||||||
try? await client.markFavoriteItem(itemId: item.id ?? "", userId: userId)
|
try? await client.markFavoriteItem(itemId: item.id ?? "", userId: userId)
|
||||||
}
|
}
|
||||||
await MainActor.run { isFavorite.toggle() }
|
isFavorite.toggle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func togglePlayed() {
|
private func togglePlayed() {
|
||||||
Task {
|
Task { [self] in
|
||||||
if isPlayed {
|
if isPlayed {
|
||||||
try? await client.markUnplayedItem(itemId: item.id ?? "", userId: userId)
|
try? await client.markUnplayedItem(itemId: item.id ?? "", userId: userId)
|
||||||
} else {
|
} else {
|
||||||
try? await client.markPlayedItem(itemId: item.id ?? "", userId: userId)
|
try? await client.markPlayedItem(itemId: item.id ?? "", userId: userId)
|
||||||
}
|
}
|
||||||
await MainActor.run { isPlayed.toggle() }
|
isPlayed.toggle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
// Created by Brendan Szymanski on 6/5/26.
|
// Created by Brendan Szymanski on 6/5/26.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Adwaita
|
@preconcurrency import Adwaita
|
||||||
import Foundation
|
import Foundation
|
||||||
import LuminateCore
|
import LuminateCore
|
||||||
import LuminateDI
|
import LuminateDI
|
||||||
|
|
@ -15,7 +15,7 @@ struct PosterCell: View {
|
||||||
var item: Components.Schemas.BaseItemDto
|
var item: Components.Schemas.BaseItemDto
|
||||||
var client: JellyfinClient
|
var client: JellyfinClient
|
||||||
@Injected(\.pageAnimationTracker) var pageAnimationTracker
|
@Injected(\.pageAnimationTracker) var pageAnimationTracker
|
||||||
@State private var imageData: Data?
|
@State private nonisolated(unsafe) var imageData: Data?
|
||||||
|
|
||||||
var view: Body {
|
var view: Body {
|
||||||
VStack {
|
VStack {
|
||||||
|
|
@ -46,7 +46,7 @@ struct PosterCell: View {
|
||||||
guard let tag = item.primaryImageTag,
|
guard let tag = item.primaryImageTag,
|
||||||
let itemId = item.id
|
let itemId = item.id
|
||||||
else { return }
|
else { return }
|
||||||
Task {
|
Task { [self] in
|
||||||
let url = await client.imageURL(
|
let url = await client.imageURL(
|
||||||
itemId: itemId,
|
itemId: itemId,
|
||||||
imageType: .primary,
|
imageType: .primary,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
// Created by Brendan Szymanski on 6/5/26.
|
// Created by Brendan Szymanski on 6/5/26.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Adwaita
|
@preconcurrency import Adwaita
|
||||||
import Foundation
|
import Foundation
|
||||||
import LuminateCore
|
import LuminateCore
|
||||||
import LuminateDI
|
import LuminateDI
|
||||||
|
|
@ -14,9 +14,9 @@ struct SearchView: View {
|
||||||
|
|
||||||
var client: JellyfinClient
|
var client: JellyfinClient
|
||||||
var userId: String
|
var userId: String
|
||||||
@State private var searchText = ""
|
@State private nonisolated(unsafe) var searchText = ""
|
||||||
@State private var results: [Components.Schemas.SearchHint] = []
|
@State private nonisolated(unsafe) var results: [Components.Schemas.SearchHint] = []
|
||||||
@State private var isSearching = false
|
@State private nonisolated(unsafe) var isSearching = false
|
||||||
|
|
||||||
var view: Body {
|
var view: Body {
|
||||||
VStack {
|
VStack {
|
||||||
|
|
@ -45,16 +45,14 @@ struct SearchView: View {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
isSearching = true
|
isSearching = true
|
||||||
Task {
|
Task { [self] in
|
||||||
let result = try? await client.getSearchHints(
|
let result = try? await client.getSearchHints(
|
||||||
searchTerm: searchText,
|
searchTerm: searchText,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
limit: 50
|
limit: 50
|
||||||
)
|
)
|
||||||
await MainActor.run {
|
results = result?.searchHints ?? []
|
||||||
results = result?.searchHints ?? []
|
isSearching = false
|
||||||
isSearching = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -68,7 +66,7 @@ struct SearchResultRow: View {
|
||||||
var hint: Components.Schemas.SearchHint
|
var hint: Components.Schemas.SearchHint
|
||||||
var client: JellyfinClient
|
var client: JellyfinClient
|
||||||
@Injected(\.pageAnimationTracker) var pageAnimationTracker
|
@Injected(\.pageAnimationTracker) var pageAnimationTracker
|
||||||
@State private var imageData: Data?
|
@State private nonisolated(unsafe) var imageData: Data?
|
||||||
|
|
||||||
var view: Body {
|
var view: Body {
|
||||||
HStack {
|
HStack {
|
||||||
|
|
@ -118,7 +116,7 @@ struct SearchResultRow: View {
|
||||||
guard let tag = hint.primaryImageTag,
|
guard let tag = hint.primaryImageTag,
|
||||||
let itemId = hint.id ?? hint.itemId
|
let itemId = hint.id ?? hint.itemId
|
||||||
else { return }
|
else { return }
|
||||||
Task {
|
Task { [self] in
|
||||||
guard
|
guard
|
||||||
let url = await client.imageURL(
|
let url = await client.imageURL(
|
||||||
itemId: itemId, imageType: .primary, tag: tag, maxWidth: 160
|
itemId: itemId, imageType: .primary, tag: tag, maxWidth: 160
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
// Created by Brendan Szymanski on 6/5/26.
|
// Created by Brendan Szymanski on 6/5/26.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Adwaita
|
@preconcurrency import Adwaita
|
||||||
import Foundation
|
import Foundation
|
||||||
import LuminateCore
|
import LuminateCore
|
||||||
|
|
||||||
|
|
@ -14,9 +14,9 @@ struct TVShowView: View {
|
||||||
var item: Components.Schemas.BaseItemDto
|
var item: Components.Schemas.BaseItemDto
|
||||||
var client: JellyfinClient
|
var client: JellyfinClient
|
||||||
var userId: String
|
var userId: String
|
||||||
@State private var seasons: [Components.Schemas.BaseItemDto] = []
|
@State private nonisolated(unsafe) var seasons: [Components.Schemas.BaseItemDto] = []
|
||||||
@State private var selectedSeasonId: String?
|
@State private nonisolated(unsafe) var selectedSeasonId: String?
|
||||||
@State private var backdropData: Data?
|
@State private nonisolated(unsafe) var backdropData: Data?
|
||||||
|
|
||||||
var view: Body {
|
var view: Body {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
|
|
@ -86,7 +86,7 @@ struct TVShowView: View {
|
||||||
|
|
||||||
private func loadBackdrop() {
|
private func loadBackdrop() {
|
||||||
guard let tag = item.backdropImageTag, let itemId = item.id else { return }
|
guard let tag = item.backdropImageTag, let itemId = item.id else { return }
|
||||||
Task {
|
Task { [self] in
|
||||||
guard
|
guard
|
||||||
let url = await client.imageURL(
|
let url = await client.imageURL(
|
||||||
itemId: itemId, imageType: .backdrop, tag: tag, maxWidth: 1920
|
itemId: itemId, imageType: .backdrop, tag: tag, maxWidth: 1920
|
||||||
|
|
@ -98,12 +98,10 @@ struct TVShowView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadSeasons() {
|
private func loadSeasons() {
|
||||||
Task {
|
Task { [self] in
|
||||||
let result = try? await client.getSeasons(seriesId: item.id ?? "", userId: userId)
|
let result = try? await client.getSeasons(seriesId: item.id ?? "", userId: userId)
|
||||||
await MainActor.run {
|
seasons = result?.items ?? []
|
||||||
seasons = result?.items ?? []
|
selectedSeasonId = seasons.first?.id
|
||||||
selectedSeasonId = seasons.first?.id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue