Show toolbar buttons and tab picker during channel loading

Display the view options button, channel menu, and content type tabs
immediately when the cached header is shown, instead of waiting for
the full channel data to load. The spinner now appears only in the
content area below the tabs.
This commit is contained in:
Arkadiusz Fal
2026-02-20 20:14:01 +01:00
parent 13614e7fa0
commit 4f5781bc20
2 changed files with 79 additions and 24 deletions

View File

@@ -10953,6 +10953,7 @@
}
},
"settings.icloud.dev.badge" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -10963,6 +10964,7 @@
}
},
"settings.icloud.dev.footer" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -10973,6 +10975,7 @@
}
},
"settings.icloud.dev.title" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -14649,17 +14652,6 @@
}
}
},
"sources.field.proxiesVideos" : {
"comment" : "Toggle label for video proxy option",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Proxy videos"
}
}
}
},
"sources.field.password" : {
"comment" : "Field label for password",
"localizations" : {
@@ -14704,6 +14696,17 @@
}
}
},
"sources.field.proxiesVideos" : {
"comment" : "Toggle label for video proxy option",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Proxy videos"
}
}
}
},
"sources.field.serverVersion" : {
"comment" : "Field label for server version in Yattee Server info",
"localizations" : {
@@ -14792,17 +14795,6 @@
}
}
},
"sources.footer.proxiesVideos" : {
"comment" : "Footer text explaining video proxy option",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Route video streams through this instance instead of connecting directly. Enable if direct video playback is blocked."
}
}
}
},
"sources.footer.auth" : {
"comment" : "Footer text for authentication section",
"localizations" : {
@@ -14847,6 +14839,17 @@
}
}
},
"sources.footer.proxiesVideos" : {
"comment" : "Footer text explaining video proxy option",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Route video streams through this instance instead of connecting directly. Enable if direct video playback is blocked."
}
}
}
},
"sources.footer.remoteServer" : {
"comment" : "Footer text for remote server URL entry",
"localizations" : {

View File

@@ -381,10 +381,17 @@ struct ChannelView: View {
header(name: cached.name, thumbnailURL: cached.thumbnailURL, bannerURL: cached.bannerURL)
.id("channelTop")
// Centered spinner for content area
// Show tab picker during loading (doesn't depend on channel)
if supportsChannelTabs {
contentTypePicker
.padding(.horizontal)
.padding(.vertical, 8)
}
// Centered spinner for content area below tabs
ProgressView()
.frame(maxWidth: .infinity)
.padding(.top, 60)
.padding(.top, 40)
}
}
.onChange(of: geometry.size.width, initial: true) { _, newWidth in
@@ -418,11 +425,56 @@ struct ChannelView: View {
}
.opacity(collapsedTitleOpacity)
}
ToolbarItem(placement: .primaryAction) {
Button {
showViewOptions = true
} label: {
Label(String(localized: "viewOptions.title"), systemImage: "slider.horizontal.3")
}
.liquidGlassTransitionSource(id: "channelViewOptions", in: sheetTransition)
}
#if !os(tvOS)
if #available(iOS 26, macOS 26, *) {
ToolbarSpacer(.fixed, placement: .primaryAction)
}
#endif
ToolbarItem(placement: .primaryAction) {
channelMenu
}
}
.sheet(isPresented: $showViewOptions) {
ViewOptionsSheet(
layout: $layout,
rowStyle: $rowStyle,
gridColumns: $gridColumns,
hideWatched: $hideWatched,
maxGridColumns: gridConfig.maxColumns
)
.liquidGlassSheetContent(sourceID: "channelViewOptions", in: sheetTransition)
}
#if os(iOS)
.toolbarBackground(collapseProgress > 0.8 ? .visible : .hidden, for: .navigationBar)
.navigationBarTitleDisplayMode(.inline)
#endif
.modifier(ChannelScrollOffsetModifier(
scrollOffset: $scrollOffset,
isPlayerExpanded: appEnvironment?.navigationCoordinator.isPlayerExpanded ?? false
))
.confirmationDialog(
String(localized: "channel.unsubscribe.confirmation.title"),
isPresented: $showingUnsubscribeConfirmation,
titleVisibility: .visible
) {
Button(String(localized: "channel.unsubscribe.confirmation.action"), role: .destructive) {
unsubscribe()
}
Button(String(localized: "common.cancel"), role: .cancel) {}
} message: {
Text(String(localized: "channel.unsubscribe.confirmation.message"))
}
}
// MARK: - Header