Eine misslungene Seite beendete das Nachladen für immer
Der Grund für „es sind wieder nicht alle Filme drin": im Fehlerfall stand hier `page = nil`. Eine einzige misslungene Anfrage auf einer wackeligen Leitung, und die Mediathek blieb endgültig bei sechzig Titeln stehen — lautlos, ohne dass irgendetwas darauf hindeutete. Im Zug passiert das zuverlässig. Der Kommentar daneben begründete das Schweigen, und das war auch richtig: ein Fehlerblatt mitten im Scrollen wäre störend. Nur hat er das eigentliche Problem verdeckt — nicht die fehlende Meldung war schlimm, sondern dass es danach keinen Weg zurück gab. Jetzt bleibt die Seite erhalten, und unten erscheint eine Zeile zum Weiterladen. Von selbst wird nach einem Fehlschlag **nicht** mehr nachgefasst: sonst liefe bei jedem Scrollen eine Anfrage in dieselbe tote Leitung. Dazu der Knopf zum Neuladen in der Leiste, wie gewünscht. Ziehen von oben gibt es auf dem Mac nicht, und in einem Raster ist es ohnehin kaum zu treffen — wer den Verdacht hat, dass etwas fehlt, braucht einen Knopf und keine Geste. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
24d7423f9c
commit
348ec96282
@@ -2481,6 +2481,57 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"library.loadMore" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Weitere laden"
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Load more"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"library.loadMoreFailed" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Der Nachschub ist hängen geblieben — es fehlen noch Titel."
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Loading more stalled — some titles are still missing."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"library.reload" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Neu laden"
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Reload"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"library.scan" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
|
||||
@@ -17,6 +17,12 @@ struct LibraryView: View {
|
||||
@State private var state: LoadState<Void> = .loading
|
||||
@State private var query = MediaQuery()
|
||||
@State private var page: MediaPage?
|
||||
/// Ob der letzte Nachschub misslungen ist.
|
||||
///
|
||||
/// Dann steht unten eine Zeile zum Weiterversuchen: von selbst passiert
|
||||
/// nichts mehr, weil die Kacheln, die das Nachladen auslösen, längst
|
||||
/// erschienen sind und kein zweites Mal erscheinen.
|
||||
@State private var loadMoreFailed = false
|
||||
@State private var isLoadingMore = false
|
||||
@State private var showsFilters = false
|
||||
|
||||
@@ -101,6 +107,7 @@ struct LibraryView: View {
|
||||
.padding(FlixySpacing.screenMargin)
|
||||
|
||||
if isLoadingMore { ProgressView().padding(.bottom, FlixySpacing.section) }
|
||||
moreButton.padding(.bottom, FlixySpacing.section)
|
||||
}
|
||||
.flixyRevealsTopChrome()
|
||||
}
|
||||
@@ -120,10 +127,28 @@ struct LibraryView: View {
|
||||
}
|
||||
|
||||
if isLoadingMore { ProgressView().padding(.vertical, FlixySpacing.section) }
|
||||
moreButton.padding(.vertical, FlixySpacing.section)
|
||||
}
|
||||
.flixyRevealsTopChrome()
|
||||
}
|
||||
|
||||
/// Der Weg weiter, wenn der Nachschub hängen geblieben ist.
|
||||
@ViewBuilder
|
||||
private var moreButton: some View {
|
||||
if loadMoreFailed, let page, page.hasMore {
|
||||
VStack(spacing: FlixySpacing.snug) {
|
||||
Text("library.loadMoreFailed")
|
||||
.font(.flixyCaption)
|
||||
.foregroundStyle(Color.flixyTextMuted)
|
||||
Button("library.loadMore") {
|
||||
Task { await loadMore() }
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
|
||||
@ToolbarContentBuilder
|
||||
private var toolbar: some ToolbarContent {
|
||||
// Nur auf schmalen Geräten: dort hat die Suche keinen eigenen Eintrag
|
||||
@@ -139,6 +164,20 @@ struct LibraryView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// **Neu laden.** Auf dem Mac und dem iPad gibt es kein Ziehen von
|
||||
// oben, und selbst dort wo es das gibt, ist es in einem Raster kaum
|
||||
// zu treffen. Wer den Verdacht hat, dass etwas fehlt, braucht einen
|
||||
// Knopf und keine Geste.
|
||||
ToolbarItem(placement: .flixyTrailing) {
|
||||
Button {
|
||||
Task { await reload() }
|
||||
} label: {
|
||||
Image(systemName: "arrow.clockwise")
|
||||
}
|
||||
.accessibilityLabel(Text("library.reload"))
|
||||
.disabled(isLoadingMore)
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .flixyTrailing) {
|
||||
Button {
|
||||
showsFilters = true
|
||||
@@ -182,16 +221,28 @@ struct LibraryView: View {
|
||||
|
||||
/// Lädt nach, sobald die Ansicht sich dem Ende der Liste nähert.
|
||||
private func loadMoreIfNeeded(reaching item: MediaItem) async {
|
||||
guard let server = session.backend,
|
||||
let page, page.hasMore,
|
||||
!isLoadingMore,
|
||||
guard
|
||||
// Fünf Einträge vor Schluss: früh genug, dass der Nachschub da
|
||||
// ist, bevor jemand am Ende ankommt, und spät genug, dass nicht
|
||||
// jedes Scrollen eine Anfrage auslöst.
|
||||
items.suffix(5).contains(where: { $0.id == item.id })
|
||||
items.suffix(5).contains(where: { $0.id == item.id }),
|
||||
// Nach einem Fehlschlag **nicht** von selbst: sonst läuft bei
|
||||
// jedem Scrollen eine Anfrage in dieselbe tote Leitung. Von dort
|
||||
// übernimmt der Knopf.
|
||||
!loadMoreFailed
|
||||
else { return }
|
||||
await loadMore()
|
||||
}
|
||||
|
||||
/// Holt die nächste Seite — von selbst oder auf Knopfdruck.
|
||||
private func loadMore() async {
|
||||
guard let server = session.backend,
|
||||
let page, page.hasMore,
|
||||
!isLoadingMore
|
||||
else { return }
|
||||
|
||||
isLoadingMore = true
|
||||
loadMoreFailed = false
|
||||
defer { isLoadingMore = false }
|
||||
|
||||
do {
|
||||
@@ -208,9 +259,16 @@ struct LibraryView: View {
|
||||
items.append(contentsOf: next.items.filter { !known.contains($0.id) })
|
||||
self.page = next
|
||||
} catch {
|
||||
// Stillschweigend: die Liste steht bereits, ein Fehlerblatt mitten
|
||||
// im Scrollen wäre störender als ein paar fehlende Einträge.
|
||||
self.page = nil
|
||||
// **Nicht `page = nil`.** Das stand hier, und es beendete das
|
||||
// Nachladen **endgültig**: eine einzige misslungene Anfrage auf
|
||||
// einer wackeligen Leitung, und die Mediathek blieb für immer bei
|
||||
// sechzig Titeln stehen — lautlos, ohne dass irgendetwas darauf
|
||||
// hindeutete. Genau so sieht „es sind nicht alle Filme drin" aus.
|
||||
//
|
||||
// Die Seite bleibt also, damit ein zweiter Versuch möglich ist.
|
||||
// Ein Fehlerblatt mitten im Scrollen wäre trotzdem störend —
|
||||
// stattdessen erscheint unten eine Zeile zum Weiterladen.
|
||||
loadMoreFailed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user