Improve TV settings screen (#943)
- Show detail side by side rather than navigate - Fix scrolling in purchased view
This commit is contained in:
parent
e49e8881b3
commit
80d40c3161
|
@ -108,19 +108,6 @@ private extension AppCoordinator {
|
|||
DebugLogContentView(lines: $0)
|
||||
}
|
||||
|
||||
case .credits:
|
||||
CreditsView()
|
||||
.resized(width: 0.5)
|
||||
.themeList()
|
||||
|
||||
case .donate:
|
||||
DonateView(modifier: DonateViewModifier())
|
||||
|
||||
case .purchased:
|
||||
PurchasedView()
|
||||
.resized(width: 0.5)
|
||||
.themeList()
|
||||
|
||||
case .tunnelLog:
|
||||
DebugLogView(withTunnel: tunnel, parameters: Constants.shared.log) {
|
||||
DebugLogContentView(lines: $0)
|
||||
|
|
|
@ -28,11 +28,5 @@ import Foundation
|
|||
enum AppCoordinatorRoute: Hashable {
|
||||
case appLog
|
||||
|
||||
case credits
|
||||
|
||||
case donate
|
||||
|
||||
case purchased
|
||||
|
||||
case tunnelLog
|
||||
}
|
||||
|
|
|
@ -38,12 +38,11 @@ struct DonateViewModifier: ViewModifier {
|
|||
}
|
||||
}
|
||||
}
|
||||
.padding(.top, 150)
|
||||
}
|
||||
}
|
||||
|
||||
private extension DonateViewModifier {
|
||||
var columns: [GridItem] {
|
||||
[GridItem(.adaptive(minimum: 500))]
|
||||
[GridItem(.adaptive(minimum: 300))]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,17 +29,51 @@ import PassepartoutKit
|
|||
import SwiftUI
|
||||
import UILibrary
|
||||
|
||||
enum Detail {
|
||||
case credits
|
||||
|
||||
case donate
|
||||
|
||||
case other
|
||||
|
||||
case purchased
|
||||
}
|
||||
|
||||
struct SettingsView: View {
|
||||
let tunnel: ExtendedTunnel
|
||||
|
||||
@Namespace
|
||||
private var masterScope
|
||||
|
||||
@Namespace
|
||||
private var detailScope
|
||||
|
||||
@FocusState
|
||||
private var focus: Detail?
|
||||
|
||||
@State
|
||||
private var detail: Detail?
|
||||
|
||||
var body: some View {
|
||||
listView
|
||||
.resized(width: 0.5)
|
||||
HStack {
|
||||
masterView
|
||||
.frame(maxWidth: .infinity)
|
||||
.focused($focus, equals: .other)
|
||||
|
||||
DetailView(detail: detail)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.onChange(of: focus) {
|
||||
guard focus != nil else {
|
||||
return
|
||||
}
|
||||
detail = focus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension SettingsView {
|
||||
var listView: some View {
|
||||
var masterView: some View {
|
||||
List {
|
||||
creditsSection
|
||||
diagnosticsSection
|
||||
|
@ -50,8 +84,10 @@ private extension SettingsView {
|
|||
|
||||
var creditsSection: some View {
|
||||
Group {
|
||||
NavigationLink(Strings.Views.About.Credits.title, value: AppCoordinatorRoute.credits)
|
||||
NavigationLink(Strings.Views.Donate.title, value: AppCoordinatorRoute.donate)
|
||||
Button(Strings.Views.About.Credits.title) {}
|
||||
.focused($focus, equals: .credits)
|
||||
Button(Strings.Views.Donate.title) {}
|
||||
.focused($focus, equals: .donate)
|
||||
}
|
||||
.themeSection(header: Strings.Unlocalized.appName)
|
||||
}
|
||||
|
@ -67,7 +103,8 @@ private extension SettingsView {
|
|||
|
||||
var aboutSection: some View {
|
||||
Group {
|
||||
NavigationLink(Strings.Views.Purchased.title, value: AppCoordinatorRoute.purchased)
|
||||
Button(Strings.Views.Purchased.title) {}
|
||||
.focused($focus, equals: .purchased)
|
||||
Text(Strings.Global.Nouns.version)
|
||||
.themeTrailingValue(BundleConfiguration.mainVersionString)
|
||||
}
|
||||
|
@ -75,6 +112,28 @@ private extension SettingsView {
|
|||
}
|
||||
}
|
||||
|
||||
private struct DetailView: View {
|
||||
let detail: Detail?
|
||||
|
||||
var body: some View {
|
||||
switch detail {
|
||||
case .credits:
|
||||
CreditsView()
|
||||
.themeList()
|
||||
|
||||
case .donate:
|
||||
DonateView(modifier: DonateViewModifier())
|
||||
|
||||
case .purchased:
|
||||
PurchasedView()
|
||||
.themeList()
|
||||
|
||||
default:
|
||||
VStack {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
#Preview {
|
||||
|
|
|
@ -178,15 +178,7 @@ private extension GenericCreditsView {
|
|||
var translationsSection: some View {
|
||||
Section {
|
||||
ForEach(sortedLanguages, id: \.self) { code in
|
||||
#if os(tvOS)
|
||||
Button {
|
||||
//
|
||||
} label: {
|
||||
translationLabel(code)
|
||||
}
|
||||
#else
|
||||
translationLabel(code)
|
||||
#endif
|
||||
}
|
||||
} header: {
|
||||
translationsHeader.map(Text.init)
|
||||
|
@ -215,6 +207,7 @@ private extension GenericCreditsView {
|
|||
}
|
||||
}
|
||||
}
|
||||
.scrollableOnTV()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,15 @@ extension View {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
public func scrollableOnTV() -> some View {
|
||||
// focusable()
|
||||
Button {
|
||||
//
|
||||
} label: {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ViewModifier {
|
||||
|
|
|
@ -77,19 +77,23 @@ private extension PurchasedView {
|
|||
Group {
|
||||
Text(Strings.Views.Purchased.Rows.buildNumber)
|
||||
.themeTrailingValue(build.description)
|
||||
.scrollableOnTV()
|
||||
}
|
||||
.themeSection(header: Strings.Views.Purchased.Sections.Download.header)
|
||||
}
|
||||
}
|
||||
|
||||
var productsSection: some View {
|
||||
Group {
|
||||
ForEach(products, id: \.productIdentifier) {
|
||||
Text($0.localizedTitle)
|
||||
.themeTrailingValue($0.localizedPrice)
|
||||
products.nilIfEmpty.map { products in
|
||||
Group {
|
||||
ForEach(products, id: \.productIdentifier) {
|
||||
Text($0.localizedTitle)
|
||||
.themeTrailingValue($0.localizedPrice)
|
||||
.scrollableOnTV()
|
||||
}
|
||||
}
|
||||
.themeSection(header: Strings.Views.Purchased.Sections.Products.header)
|
||||
}
|
||||
.themeSection(header: Strings.Views.Purchased.Sections.Products.header)
|
||||
}
|
||||
|
||||
var featuresSection: some View {
|
||||
|
@ -101,6 +105,7 @@ private extension PurchasedView {
|
|||
ThemeImage(.marked)
|
||||
.opaque(iapManager.isEligible(for: feature))
|
||||
}
|
||||
.scrollableOnTV()
|
||||
}
|
||||
}
|
||||
.themeSection(header: Strings.Views.Purchased.Sections.Features.header)
|
||||
|
|
|
@ -155,13 +155,8 @@ private extension PaywallView {
|
|||
}
|
||||
|
||||
func featureView(for feature: AppFeature) -> some View {
|
||||
#if os(tvOS)
|
||||
Button(feature.localizedDescription) {
|
||||
//
|
||||
}
|
||||
#else
|
||||
Text(feature.localizedDescription)
|
||||
#endif
|
||||
.scrollableOnTV()
|
||||
}
|
||||
|
||||
var restoreView: some View {
|
||||
|
|
Loading…
Reference in New Issue