diff --git a/Yattee/Localizable.xcstrings b/Yattee/Localizable.xcstrings index 63585317..7994517b 100644 --- a/Yattee/Localizable.xcstrings +++ b/Yattee/Localizable.xcstrings @@ -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" : { diff --git a/Yattee/Views/Channel/ChannelView.swift b/Yattee/Views/Channel/ChannelView.swift index 38063129..3b0e684e 100644 --- a/Yattee/Views/Channel/ChannelView.swift +++ b/Yattee/Views/Channel/ChannelView.swift @@ -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