From ec395ff2e0ede2bcc4434e93494dd54948842c32 Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Mon, 18 Oct 2021 01:06:00 +0200 Subject: [PATCH] Add Welcome screen --- Fixtures/View+Fixtures.swift | 6 +- Model/AccountsModel.swift | 12 +++- Model/NavigationModel.swift | 1 + Pearvidious.xcodeproj/project.pbxproj | 16 +++++ Shared/Navigation/AccountsMenuView.swift | 1 - Shared/Navigation/AppSidebarNavigation.swift | 7 ++- Shared/Navigation/ContentView.swift | 22 ++++++- Shared/Navigation/Sidebar.swift | 14 +++-- Shared/Player/PlaybackBar.swift | 2 +- Shared/Player/Player.swift | 14 +++-- Shared/Settings/InstancesSettingsView.swift | 5 +- Shared/Views/OpenSettingsButton.swift | 27 +++++++++ Shared/Views/SignInRequiredView.swift | 17 +----- Shared/Views/WelcomeScreen.swift | 63 ++++++++++++++++++++ macOS/Settings/InstancesSettingsView.swift | 18 ++++-- tvOS/AccountSelectionView.swift | 4 +- 16 files changed, 187 insertions(+), 42 deletions(-) create mode 100644 Shared/Views/OpenSettingsButton.swift create mode 100644 Shared/Views/WelcomeScreen.swift diff --git a/Fixtures/View+Fixtures.swift b/Fixtures/View+Fixtures.swift index 5a6fb996..d1fac91e 100644 --- a/Fixtures/View+Fixtures.swift +++ b/Fixtures/View+Fixtures.swift @@ -4,9 +4,11 @@ import SwiftUI struct FixtureEnvironmentObjectsModifier: ViewModifier { func body(content: Content) -> some View { content + .environmentObject(AccountsModel()) .environmentObject(InstancesModel()) - .environmentObject(api) + .environmentObject(invidious) .environmentObject(NavigationModel()) + .environmentObject(PipedAPI()) .environmentObject(player) .environmentObject(PlaylistsModel()) .environmentObject(RecentsModel()) @@ -14,7 +16,7 @@ struct FixtureEnvironmentObjectsModifier: ViewModifier { .environmentObject(subscriptions) } - private var api: InvidiousAPI { + private var invidious: InvidiousAPI { let api = InvidiousAPI() api.validInstance = true diff --git a/Model/AccountsModel.swift b/Model/AccountsModel.swift index d156ce97..4815b5a2 100644 --- a/Model/AccountsModel.swift +++ b/Model/AccountsModel.swift @@ -14,8 +14,12 @@ final class AccountsModel: ObservableObject { Defaults[.instances].map(\.anonymousAccount) + Defaults[.accounts] } + var isEmpty: Bool { + account.isNil + } + var signedIn: Bool { - !account.isNil && !account.anonymous + !isEmpty && !account.anonymous } init() { @@ -28,13 +32,17 @@ final class AccountsModel: ObservableObject { ) } - func setAccount(_ account: Instance.Account) { + func setAccount(_ account: Instance.Account! = nil) { guard account != self.account else { return } self.account = account + guard !account.isNil else { + return + } + switch account.instance.app { case .invidious: invidious.setAccount(account) diff --git a/Model/NavigationModel.swift b/Model/NavigationModel.swift index 2b094f6f..6628abed 100644 --- a/Model/NavigationModel.swift +++ b/Model/NavigationModel.swift @@ -30,6 +30,7 @@ final class NavigationModel: ObservableObject { @Published var sidebarSectionChanged = false @Published var presentingSettings = false + @Published var presentingWelcomeScreen = false var tabSelectionBinding: Binding { Binding( diff --git a/Pearvidious.xcodeproj/project.pbxproj b/Pearvidious.xcodeproj/project.pbxproj index 89302705..3da5db99 100644 --- a/Pearvidious.xcodeproj/project.pbxproj +++ b/Pearvidious.xcodeproj/project.pbxproj @@ -302,6 +302,12 @@ 37E64DD126D597EB00C71877 /* SubscriptionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E64DD026D597EB00C71877 /* SubscriptionsModel.swift */; }; 37E64DD226D597EB00C71877 /* SubscriptionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E64DD026D597EB00C71877 /* SubscriptionsModel.swift */; }; 37E64DD326D597EB00C71877 /* SubscriptionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E64DD026D597EB00C71877 /* SubscriptionsModel.swift */; }; + 37E70923271CD43000D34DDE /* WelcomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70922271CD43000D34DDE /* WelcomeScreen.swift */; }; + 37E70924271CD43000D34DDE /* WelcomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70922271CD43000D34DDE /* WelcomeScreen.swift */; }; + 37E70925271CD43000D34DDE /* WelcomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70922271CD43000D34DDE /* WelcomeScreen.swift */; }; + 37E70927271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70926271CDDAE00D34DDE /* OpenSettingsButton.swift */; }; + 37E70928271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70926271CDDAE00D34DDE /* OpenSettingsButton.swift */; }; + 37E70929271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70926271CDDAE00D34DDE /* OpenSettingsButton.swift */; }; 37EAD86B267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EAD86A267B9C5600D9E01B /* SponsorBlockAPI.swift */; }; 37EAD86C267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EAD86A267B9C5600D9E01B /* SponsorBlockAPI.swift */; }; 37EAD86D267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EAD86A267B9C5600D9E01B /* SponsorBlockAPI.swift */; }; @@ -467,6 +473,8 @@ 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerStreams.swift; sourceTree = ""; }; 37E2EEAA270656EC00170416 /* PlayerControlsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerControlsView.swift; sourceTree = ""; }; 37E64DD026D597EB00C71877 /* SubscriptionsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsModel.swift; sourceTree = ""; }; + 37E70922271CD43000D34DDE /* WelcomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreen.swift; sourceTree = ""; }; + 37E70926271CDDAE00D34DDE /* OpenSettingsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSettingsButton.swift; sourceTree = ""; }; 37EAD86A267B9C5600D9E01B /* SponsorBlockAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SponsorBlockAPI.swift; sourceTree = ""; }; 37EAD86E267B9ED100D9E01B /* Segment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Segment.swift; sourceTree = ""; }; 37F49BA226CAA59B00304AC0 /* Playlist+Fixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Playlist+Fixtures.swift"; sourceTree = ""; }; @@ -615,6 +623,7 @@ children = ( 37BA793E26DB8F97002A0235 /* ChannelVideosView.swift */, 37152EE926EFEB95004FB96D /* LazyView.swift */, + 37E70926271CDDAE00D34DDE /* OpenSettingsButton.swift */, 37E2EEAA270656EC00170416 /* PlayerControlsView.swift */, 37BA793A26DB8EE4002A0235 /* PlaylistVideosView.swift */, 37AAF27D26737323007FC770 /* PopularView.swift */, @@ -622,6 +631,7 @@ 376B2E0626F920D600B1D64D /* SignInRequiredView.swift */, 37AAF29F26741C97007FC770 /* SubscriptionsView.swift */, 37B17D9F268A1F25006AEE9B /* VideoContextMenuView.swift */, + 37E70922271CD43000D34DDE /* WelcomeScreen.swift */, ); path = Views; sourceTree = ""; @@ -1332,11 +1342,13 @@ 37732FF42703D32400F04329 /* Sidebar.swift in Sources */, 37D4B19726717E1500C925CA /* Video.swift in Sources */, 37484C2926FC83FF00287258 /* AccountFormView.swift in Sources */, + 37E70927271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */, 371F2F1A269B43D300E4A7AB /* NavigationModel.swift in Sources */, 37BE0BCF26A0E2D50092E2DB /* VideoPlayerView.swift in Sources */, 37001563271B1F250049C794 /* AccountsModel.swift in Sources */, 37CC3F50270D010D00608308 /* VideoBanner.swift in Sources */, 378E50FB26FE8B9F00F49626 /* Instance.swift in Sources */, + 37E70923271CD43000D34DDE /* WelcomeScreen.swift in Sources */, 37484C1D26FC83A400287258 /* InstancesSettingsView.swift in Sources */, 37BD07BB2698AB60003EBB87 /* AppSidebarNavigation.swift in Sources */, 37D4B0E42671614900C925CA /* PearvidiousApp.swift in Sources */, @@ -1355,6 +1367,7 @@ 3788AC2C26F6842D00F6BAA9 /* WatchNowSectionBody.swift in Sources */, 37CEE4BE2677B670005A1EFE /* SingleAssetStream.swift in Sources */, 37BA794826DC2E56002A0235 /* AppSidebarSubscriptions.swift in Sources */, + 37E70928271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */, 37EAD86C267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */, 37CEE4C22677B697005A1EFE /* Stream.swift in Sources */, 371F2F1B269B43D300E4A7AB /* NavigationModel.swift in Sources */, @@ -1387,6 +1400,7 @@ 37B044B826F7AB9000E1419D /* SettingsView.swift in Sources */, 3765788A2685471400D4EA09 /* Playlist.swift in Sources */, 37E2EEAC270656EC00170416 /* PlayerControlsView.swift in Sources */, + 37E70924271CD43000D34DDE /* WelcomeScreen.swift in Sources */, 373CFACC26966264003CB2C6 /* SearchQuery.swift in Sources */, 37AAF29126740715007FC770 /* Channel.swift in Sources */, 37BD07BC2698AB60003EBB87 /* AppSidebarNavigation.swift in Sources */, @@ -1485,6 +1499,7 @@ 37141671267A8ACC006CA35D /* TrendingView.swift in Sources */, 3788AC2926F6840700F6BAA9 /* WatchNowSection.swift in Sources */, 37319F0727103F94004ECCD0 /* PlayerQueue.swift in Sources */, + 37E70925271CD43000D34DDE /* WelcomeScreen.swift in Sources */, 37DD87C9271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */, 375168D82700FDB9008F96A6 /* Debounce.swift in Sources */, 37BA794126DB8F97002A0235 /* ChannelVideosView.swift in Sources */, @@ -1506,6 +1521,7 @@ 3743CA50270EFE3400E4D32B /* PlayerQueueRow.swift in Sources */, 37A9966026D6F9B9006E3224 /* WatchNowView.swift in Sources */, 37001565271B1F250049C794 /* AccountsModel.swift in Sources */, + 37E70929271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */, 37484C1F26FC83A400287258 /* InstancesSettingsView.swift in Sources */, 37C7A1D7267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */, 376578932685490700D4EA09 /* PlaylistsView.swift in Sources */, diff --git a/Shared/Navigation/AccountsMenuView.swift b/Shared/Navigation/AccountsMenuView.swift index ebe82f33..21931bae 100644 --- a/Shared/Navigation/AccountsMenuView.swift +++ b/Shared/Navigation/AccountsMenuView.swift @@ -3,7 +3,6 @@ import SwiftUI struct AccountsMenuView: View { @EnvironmentObject private var model - @EnvironmentObject private var instancesModel @Default(.instances) private var instances diff --git a/Shared/Navigation/AppSidebarNavigation.swift b/Shared/Navigation/AppSidebarNavigation.swift index 75fc5e8e..89b26f60 100644 --- a/Shared/Navigation/AppSidebarNavigation.swift +++ b/Shared/Navigation/AppSidebarNavigation.swift @@ -37,7 +37,12 @@ struct AppSidebarNavigation: View { .toolbar { toolbarContent } .frame(minWidth: sidebarMinWidth) - Text("Select section") + VStack { + Image(systemName: "play.tv") + .renderingMode(.original) + .font(.system(size: 60)) + .foregroundColor(.accentColor) + } } .environment(\.navigationStyle, .sidebar) } diff --git a/Shared/Navigation/ContentView.swift b/Shared/Navigation/ContentView.swift index 80233258..9edacda4 100644 --- a/Shared/Navigation/ContentView.swift +++ b/Shared/Navigation/ContentView.swift @@ -39,6 +39,11 @@ struct ContentView: View { .environmentObject(recents) .environmentObject(search) .environmentObject(subscriptions) + .sheet(isPresented: $navigation.presentingWelcomeScreen) { + WelcomeScreen() + .environmentObject(accounts) + .environmentObject(navigation) + } #if os(iOS) .fullScreenCover(isPresented: $player.presentingPlayer) { VideoPlayerView() @@ -68,8 +73,9 @@ struct ContentView: View { PlaylistFormView(playlist: $navigation.editedPlaylist) .environmentObject(playlists) } - .sheet(isPresented: $navigation.presentingSettings) { + .sheet(isPresented: $navigation.presentingSettings, onDismiss: openWelcomeScreenIfAccountEmpty) { SettingsView() + .environmentObject(accounts) .environmentObject(instances) } #endif @@ -78,17 +84,29 @@ struct ContentView: View { func configure() { SiestaLog.Category.enabled = .common + // TODO: Remove when piped supports videos information if let account = instances.defaultAccount ?? - // TODO: Remove when piped supports videos information accounts.all.first(where: { $0.instance.app == .invidious }) { accounts.setAccount(account) } + + if accounts.account.isNil { + navigation.presentingWelcomeScreen = true + } player.accounts = accounts playlists.accounts = accounts search.accounts = accounts subscriptions.accounts = accounts } + + func openWelcomeScreenIfAccountEmpty() { + guard accounts.isEmpty else { + return + } + + navigation.presentingWelcomeScreen = true + } } struct ContentView_Previews: PreviewProvider { diff --git a/Shared/Navigation/Sidebar.swift b/Shared/Navigation/Sidebar.swift index b0d5a5ba..7a78e3bf 100644 --- a/Shared/Navigation/Sidebar.swift +++ b/Shared/Navigation/Sidebar.swift @@ -7,14 +7,16 @@ struct Sidebar: View { var body: some View { ScrollViewReader { scrollView in List { - mainNavigationLinks + if !accounts.isEmpty { + mainNavigationLinks - AppSidebarRecents() - .id("recentlyOpened") + AppSidebarRecents() + .id("recentlyOpened") - if accounts.signedIn { - AppSidebarSubscriptions() - AppSidebarPlaylists() + if accounts.signedIn { + AppSidebarSubscriptions() + AppSidebarPlaylists() + } } } .onChange(of: navigation.sidebarSectionChanged) { _ in diff --git a/Shared/Player/PlaybackBar.swift b/Shared/Player/PlaybackBar.swift index 102bcfba..8e3277a0 100644 --- a/Shared/Player/PlaybackBar.swift +++ b/Shared/Player/PlaybackBar.swift @@ -139,7 +139,7 @@ struct PlaybackBar: View { } private func availableStreamsForInstance(_ instance: Instance) -> [Stream.Kind: [Stream]] { - let streams = player.availableStreams.filter { $0.instance == instance }.sorted(by: player.streamsSorter) + let streams = player.availableStreamsSorted.filter { $0.instance == instance } return Dictionary(grouping: streams, by: \.kind!) } diff --git a/Shared/Player/Player.swift b/Shared/Player/Player.swift index 325c89a8..f85eab3a 100644 --- a/Shared/Player/Player.swift +++ b/Shared/Player/Player.swift @@ -36,17 +36,23 @@ struct Player: UIViewControllerRepresentable { #if os(tvOS) var streamingQualityMenu: UIMenu { UIMenu( - title: "Streaming quality", + title: "Streams", image: UIImage(systemName: "antenna.radiowaves.left.and.right"), children: streamingQualityMenuActions ) } var streamingQualityMenuActions: [UIAction] { - player.availableStreamsSorted.map { stream in - let image = player.streamSelection == stream ? UIImage(systemName: "checkmark") : nil + guard !player.availableStreams.isEmpty else { + return [ // swiftlint:disable:this implicit_return + UIAction(title: "Empty", attributes: .disabled) { _ in } + ] + } - return UIAction(title: stream.description, image: image) { _ in + return player.availableStreamsSorted.map { stream in + let state = player.streamSelection == stream ? UIAction.State.on : .off + + return UIAction(title: stream.description, state: state) { _ in self.player.streamSelection = stream self.player.upgradeToStream(stream) } diff --git a/Shared/Settings/InstancesSettingsView.swift b/Shared/Settings/InstancesSettingsView.swift index 36830ef1..d22ff3bf 100644 --- a/Shared/Settings/InstancesSettingsView.swift +++ b/Shared/Settings/InstancesSettingsView.swift @@ -4,7 +4,7 @@ import SwiftUI struct InstancesSettingsView: View { @Default(.instances) private var instances - @EnvironmentObject private var api + @EnvironmentObject private var accounts @EnvironmentObject private var instancesModel @EnvironmentObject private var subscriptions @EnvironmentObject private var playlists @@ -55,6 +55,9 @@ struct InstancesSettingsView: View { private func removeInstanceButton(_ instance: Instance) -> some View { Button("Remove", role: .destructive) { + if accounts.account?.instance == instance { + accounts.setAccount(nil) + } instancesModel.remove(instance) } } diff --git a/Shared/Views/OpenSettingsButton.swift b/Shared/Views/OpenSettingsButton.swift new file mode 100644 index 00000000..7b8d69e3 --- /dev/null +++ b/Shared/Views/OpenSettingsButton.swift @@ -0,0 +1,27 @@ +import SwiftUI + +struct OpenSettingsButton: View { + @Environment(\.dismiss) private var dismiss + @EnvironmentObject private var navigation + + var body: some View { + Button { + dismiss() + + #if os(macOS) + NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil) + #else + navigation.presentingSettings = true + #endif + } label: { + Label("Open Settings", systemImage: "gearshape.2") + } + .buttonStyle(.borderedProminent) + } +} + +struct OpenSettingsButton_Previews: PreviewProvider { + static var previews: some View { + OpenSettingsButton() + } +} diff --git a/Shared/Views/SignInRequiredView.swift b/Shared/Views/SignInRequiredView.swift index ae0c49d0..774dd29d 100644 --- a/Shared/Views/SignInRequiredView.swift +++ b/Shared/Views/SignInRequiredView.swift @@ -50,29 +50,16 @@ struct SignInRequiredView: View { #if !os(tvOS) if instances.isEmpty { - openSettingsButton + OpenSettingsButton() } #endif #if os(tvOS) - openSettingsButton + OpenSettingsButton() #endif } .frame(minWidth: 0, maxWidth: .infinity, alignment: .center) } - - var openSettingsButton: some View { - Button(action: { - #if os(macOS) - NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil) - #else - navigation.presentingSettings = true - #endif - }) { - Text("Open Settings") - } - .buttonStyle(.borderedProminent) - } } struct SignInRequiredView_Previews: PreviewProvider { diff --git a/Shared/Views/WelcomeScreen.swift b/Shared/Views/WelcomeScreen.swift new file mode 100644 index 00000000..a3bd2db8 --- /dev/null +++ b/Shared/Views/WelcomeScreen.swift @@ -0,0 +1,63 @@ +import SwiftUI + +struct WelcomeScreen: View { + @Environment(\.dismiss) private var dismiss + + @EnvironmentObject private var accounts + @EnvironmentObject private var navigation + + var body: some View { + VStack { + Spacer() + + Text("Welcome") + .font(.largeTitle) + .padding(.bottom, 10) + + if accounts.all.isEmpty { + Text("To start, configure your Instances in Settings") + .foregroundColor(.secondary) + } else { + Text("To start, pick one of your accounts:") + .foregroundColor(.secondary) + #if os(tvOS) + AccountSelectionView(showHeader: false) + + Button { + dismiss() + } label: { + Text("Start") + } + .opacity(accounts.account.isNil ? 0 : 1) + .disabled(accounts.account.isNil) + + #else + AccountsMenuView() + .onChange(of: accounts.account) { _ in + dismiss() + } + #if os(macOS) + .frame(maxWidth: 280) + #endif + #endif + } + + Spacer() + + OpenSettingsButton() + + Spacer() + } + .interactiveDismissDisabled() + #if os(macOS) + .frame(minWidth: 400, minHeight: 400) + #endif + } +} + +struct WelcomeScreen_Previews: PreviewProvider { + static var previews: some View { + WelcomeScreen() + .injectFixtureEnvironmentObjects() + } +} diff --git a/macOS/Settings/InstancesSettingsView.swift b/macOS/Settings/InstancesSettingsView.swift index 52906a0f..6e8ea87b 100644 --- a/macOS/Settings/InstancesSettingsView.swift +++ b/macOS/Settings/InstancesSettingsView.swift @@ -2,9 +2,6 @@ import Defaults import SwiftUI struct InstancesSettingsView: View { - @Default(.instances) private var instances - @EnvironmentObject private var model - @State private var selectedInstanceID: Instance.ID? @State private var selectedAccount: Instance.Account? @@ -14,6 +11,11 @@ struct InstancesSettingsView: View { @State private var presentingConfirmationDialog = false + @EnvironmentObject private var accounts + @EnvironmentObject private var model + + @Default(.instances) private var instances + var body: some View { Section { Text("Instance") @@ -34,11 +36,11 @@ struct InstancesSettingsView: View { if !selectedInstance.isNil, selectedInstance.supportsAccounts { Text("Accounts") List(selection: $selectedAccount) { - if accounts.isEmpty { + if selectedInstanceAccounts.isEmpty { Text("You have no accounts for this instance") .foregroundColor(.secondary) } - ForEach(accounts) { account in + ForEach(selectedInstanceAccounts) { account in AccountSettingsView(account: account, selectedAccount: $selectedAccount) .tag(account) } @@ -73,6 +75,10 @@ struct InstancesSettingsView: View { isPresented: $presentingConfirmationDialog ) { Button("Remove Instance", role: .destructive) { + if accounts.account?.instance == selectedInstance { + accounts.setAccount(nil) + } + model.remove(selectedInstance!) selectedInstanceID = instances.last?.id } @@ -113,7 +119,7 @@ struct InstancesSettingsView: View { model.find(selectedInstanceID) } - private var accounts: [Instance.Account] { + private var selectedInstanceAccounts: [Instance.Account] { guard selectedInstance != nil else { return [] } diff --git a/tvOS/AccountSelectionView.swift b/tvOS/AccountSelectionView.swift index 79802cc8..4b4ed0bc 100644 --- a/tvOS/AccountSelectionView.swift +++ b/tvOS/AccountSelectionView.swift @@ -3,13 +3,15 @@ import Foundation import SwiftUI struct AccountSelectionView: View { + var showHeader = true + @EnvironmentObject private var instancesModel @EnvironmentObject private var accounts @Default(.instances) private var instances var body: some View { - Section(header: Text("Current Account")) { + Section(header: Text(showHeader ? "Current Account" : "")) { Button(accountButtonTitle(account: accounts.account)) { if let account = nextAccount { accounts.setAccount(account)