Herunterladen steht jetzt im Kontextmenü
Bisher führte der Weg über die Detailseite — zwei Schritte für etwas, das man beim Vorbeiscrollen entscheidet. Langes Drücken auf eine Kachel bietet es jetzt direkt an, samt derselben Qualitätsauswahl. Die Kachel kennt nur eine Zusammenfassung ohne Dateiangaben; geholt wird deshalb erst beim Tippen. Eine Reihe aus zwanzig Kacheln wären sonst zwanzig Anfragen für ein Menü, das selten aufgeht. **Nur bei Filmen und Folgen.** Bei einer Serie oder Staffel lädt man nicht *den* Titel, sondern seine Folgen — dafür gibt es auf der Seite „Alle Folgen laden". Und nicht bei dem, was schon in der Warteschlange steht. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6b6bc6ad02
commit
25e6ada0ce
@@ -1,5 +1,6 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import FlixyDesign
|
import FlixyDesign
|
||||||
|
import FlixyDownloads
|
||||||
import MediaKit
|
import MediaKit
|
||||||
|
|
||||||
/// Eine Kachel, die zur Detailseite führt — mit einem Knopf, der sofort
|
/// Eine Kachel, die zur Detailseite führt — mit einem Knopf, der sofort
|
||||||
@@ -29,10 +30,19 @@ struct QuickPlayTile<Card: View>: View {
|
|||||||
@Environment(FlixySession.self) private var session
|
@Environment(FlixySession.self) private var session
|
||||||
@Environment(PlaybackCoordinator.self) private var playback
|
@Environment(PlaybackCoordinator.self) private var playback
|
||||||
@Environment(QuickPlay.self) private var quickPlay
|
@Environment(QuickPlay.self) private var quickPlay
|
||||||
|
@Environment(DownloadCoordinator.self) private var downloads
|
||||||
|
@Environment(LanguagePreferences.self) private var languages
|
||||||
@Environment(\.flixyAccent) private var accent
|
@Environment(\.flixyAccent) private var accent
|
||||||
|
|
||||||
@State private var isPointerInside = false
|
@State private var isPointerInside = false
|
||||||
@State private var errorMessage: String?
|
@State private var errorMessage: String?
|
||||||
|
/// Der nachgeladene Titel, sobald die Qualitätsauswahl offen sein soll.
|
||||||
|
///
|
||||||
|
/// **Zwei Dinge in einem Zustand**, und das mit Absicht: die Kachel kennt
|
||||||
|
/// nur eine Zusammenfassung ohne Dateiangaben, also muss vor der Frage
|
||||||
|
/// nachgeladen werden. Ein eigenes `Bool` daneben wäre ein zweiter
|
||||||
|
/// Zustand, der mit diesem gleichziehen müsste.
|
||||||
|
@State private var downloadChoice: MediaItem?
|
||||||
|
|
||||||
/// Was die umgebende Seite tun soll, wenn sich am Server etwas geändert
|
/// Was die umgebende Seite tun soll, wenn sich am Server etwas geändert
|
||||||
/// hat. Siehe `ItemActionEntries`.
|
/// hat. Siehe `ItemActionEntries`.
|
||||||
@@ -93,6 +103,15 @@ struct QuickPlayTile<Card: View>: View {
|
|||||||
Divider()
|
Divider()
|
||||||
}
|
}
|
||||||
Button("tile.play", systemImage: "play.fill") { trigger() }
|
Button("tile.play", systemImage: "play.fill") { trigger() }
|
||||||
|
|
||||||
|
// **Herunterladen gehört hierher.** Bisher führte der Weg
|
||||||
|
// über die Detailseite — zwei Schritte für etwas, das man
|
||||||
|
// beim Vorbeiscrollen entscheidet.
|
||||||
|
if isDownloadable {
|
||||||
|
Button("downloads.download", systemImage: "arrow.down.circle") {
|
||||||
|
Task { await prepareDownload() }
|
||||||
|
}
|
||||||
|
}
|
||||||
Divider()
|
Divider()
|
||||||
}
|
}
|
||||||
ItemActionEntries(
|
ItemActionEntries(
|
||||||
@@ -117,6 +136,24 @@ struct QuickPlayTile<Card: View>: View {
|
|||||||
// Aktion, die Millisekunden dauert, zu spät und ginge zu früh. Geht
|
// Aktion, die Millisekunden dauert, zu spät und ginge zu früh. Geht
|
||||||
// sie dagegen schief, muss man es erfahren — sonst bliebe der Haken
|
// sie dagegen schief, muss man es erfahren — sonst bliebe der Haken
|
||||||
// einfach aus, und das sieht aus wie ein Fehler der App.
|
// einfach aus, und das sieht aus wie ein Fehler der App.
|
||||||
|
.confirmationDialog(
|
||||||
|
Text("downloads.quality.title"),
|
||||||
|
isPresented: Binding(
|
||||||
|
get: { downloadChoice != nil },
|
||||||
|
set: { if !$0 { downloadChoice = nil } }
|
||||||
|
),
|
||||||
|
titleVisibility: .visible
|
||||||
|
) {
|
||||||
|
if let ziel = downloadChoice {
|
||||||
|
let wahl = DownloadQualityChoice(item: ziel, backend: session.backend)
|
||||||
|
ForEach(wahl.available) { quality in
|
||||||
|
Button(wahl.label(for: quality)) { enqueue(ziel, quality: quality) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button("action.cancel", role: .cancel) {}
|
||||||
|
} message: {
|
||||||
|
Text("downloads.quality.hint")
|
||||||
|
}
|
||||||
.alert(
|
.alert(
|
||||||
Text("error.title"),
|
Text("error.title"),
|
||||||
isPresented: Binding(get: { errorMessage != nil }, set: { if !$0 { errorMessage = nil } })
|
isPresented: Binding(get: { errorMessage != nil }, set: { if !$0 { errorMessage = nil } })
|
||||||
@@ -177,6 +214,35 @@ struct QuickPlayTile<Card: View>: View {
|
|||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ob es etwas herunterzuladen gibt — und ob es nicht schon da ist.
|
||||||
|
///
|
||||||
|
/// Sammlungen, Serien und Staffeln fallen heraus: dort lädt man nicht
|
||||||
|
/// *den* Titel, sondern seine Folgen, und dafür gibt es auf der Seite
|
||||||
|
/// „Alle Folgen laden".
|
||||||
|
private var isDownloadable: Bool {
|
||||||
|
guard item.kind == .movie || item.kind == .episode else { return false }
|
||||||
|
return !downloads.items.contains { $0.ratingKey == item.id }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Holt die Dateiangaben nach und öffnet dann die Auswahl.
|
||||||
|
///
|
||||||
|
/// Erst beim Tippen, nicht beim Zeichnen: eine Reihe aus zwanzig Kacheln
|
||||||
|
/// wären sonst zwanzig Anfragen für ein Menü, das selten aufgeht.
|
||||||
|
private func prepareDownload() async {
|
||||||
|
downloadChoice = (try? await session.backend?.item(id: item.id)) ?? item
|
||||||
|
}
|
||||||
|
|
||||||
|
private func enqueue(_ ziel: MediaItem, quality: DownloadQuality) {
|
||||||
|
let service = DownloadService(
|
||||||
|
backend: session.backend, coordinator: downloads, languages: languages
|
||||||
|
)
|
||||||
|
do {
|
||||||
|
_ = try service.download(ziel, quality: quality)
|
||||||
|
} catch {
|
||||||
|
errorMessage = error.explanation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func trigger() {
|
private func trigger() {
|
||||||
quickPlay.start(item, backend: session.backend, coordinator: playback)
|
quickPlay.start(item, backend: session.backend, coordinator: playback)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user