Locations manifest, reorganized instances settings

This commit is contained in:
Arkadiusz Fal
2022-07-01 23:28:32 +02:00
parent 6f62f14adf
commit 4fcf57d755
28 changed files with 686 additions and 214 deletions

View File

@@ -118,6 +118,7 @@ struct AccountForm: View {
var footer: some View {
HStack {
AccountValidationStatus(
app: .constant(instance.app),
isValid: $isValid,
isValidated: $isValidated,
isValidating: $isValidating,

View File

@@ -2,6 +2,7 @@ import Foundation
import SwiftUI
struct AccountValidationStatus: View {
@Binding var app: VideosApp?
@Binding var isValid: Bool
@Binding var isValidated: Bool
@Binding var isValidating: Bool
@@ -16,7 +17,7 @@ struct AccountValidationStatus: View {
.opacity(isValidating ? 1 : (isValidated ? 1 : 0))
VStack(alignment: .leading) {
Text(isValid ? "Connected successfully" : "Connection failed")
Text(isValid ? "Connected successfully (\(app?.name ?? "Unknown"))" : "Connection failed")
if let error = error, !isValid {
Text(error)
.font(.caption2)

View File

@@ -11,6 +11,10 @@ struct AccountsNavigationLink: View {
.buttonStyle(.plain)
.contextMenu {
removeInstanceButton(instance)
#if os(tvOS)
Button("Cancel", role: .cancel) {}
#endif
}
}

View File

@@ -0,0 +1,57 @@
import Defaults
import SwiftUI
struct AdvancedSettings: View {
@Default(.instancesManifest) private var instancesManifest
@Default(.showMPVPlaybackStats) private var showMPVPlaybackStats
var body: some View {
VStack(alignment: .leading) {
#if os(macOS)
advancedSettings
Spacer()
#else
List {
advancedSettings
}
#if os(iOS)
.listStyle(.insetGrouped)
#endif
#endif
}
#if os(tvOS)
.frame(maxWidth: 1000)
#endif
.navigationTitle("Advanced")
}
@ViewBuilder var advancedSettings: some View {
Section(header: manifestHeader, footer: manifestFooter) {
TextField("URL", text: $instancesManifest)
}
.padding(.bottom, 4)
Section(header: SettingsHeader(text: "Debugging")) {
showMPVPlaybackStatsToggle
}
}
var manifestHeader: some View {
SettingsHeader(text: "Public Manifest")
}
var manifestFooter: some View {
Text("You can create your own locations manifest and set its URL here to replace the built-in one")
.foregroundColor(.secondary)
}
var showMPVPlaybackStatsToggle: some View {
Toggle("Show MPV playback statistics", isOn: $showMPVPlaybackStats)
}
}
struct AdvancedSettings_Previews: PreviewProvider {
static var previews: some View {
AdvancedSettings()
}
}

View File

@@ -5,8 +5,8 @@ struct InstanceForm: View {
@State private var name = ""
@State private var url = ""
@State private var app = VideosApp.invidious
@State private var app: VideosApp?
@State private var isValid = false
@State private var isValidated = false
@State private var isValidating = false
@@ -27,7 +27,6 @@ struct InstanceForm: View {
}
.frame(maxWidth: 1000)
}
.onChange(of: app) { _ in validate() }
.onChange(of: url) { _ in validate() }
#if os(iOS)
.padding(.vertical)
@@ -35,13 +34,13 @@ struct InstanceForm: View {
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(Color.background(scheme: colorScheme))
#else
.frame(width: 400, height: 190)
.frame(width: 400, height: 150)
#endif
}
private var header: some View {
HStack(alignment: .center) {
Text("Add Instance")
Text("Add Location")
.font(.title2.bold())
Spacer()
@@ -71,17 +70,9 @@ struct InstanceForm: View {
private var formFields: some View {
Group {
Picker("Application", selection: $app) {
ForEach(VideosApp.allCases, id: \.self) { app in
Text(app.rawValue.capitalized).tag(app)
}
}
.pickerStyle(.segmented)
.labelsHidden()
TextField("Name", text: $name)
TextField("API URL", text: $url)
TextField("URL", text: $url)
#if !os(macOS)
.autocapitalization(.none)
@@ -92,7 +83,13 @@ struct InstanceForm: View {
private var footer: some View {
HStack(alignment: .center) {
AccountValidationStatus(isValid: $isValid, isValidated: $isValidated, isValidating: $isValidating, error: $validationError)
AccountValidationStatus(
app: $app,
isValid: $isValid,
isValidated: $isValidated,
isValidating: $isValidating,
error: $validationError
)
Spacer()
@@ -137,7 +134,7 @@ struct InstanceForm: View {
}
func submitForm() {
guard isValid else {
guard isValid, let app = app else {
return
}

View File

@@ -0,0 +1,119 @@
import Defaults
import SwiftUI
struct LocationsSettings: View {
@State private var countries = [String]()
@State private var presentingInstanceForm = false
@State private var savedFormInstanceID: Instance.ID?
@EnvironmentObject<AccountsModel> private var accounts
@EnvironmentObject<NavigationModel> private var navigation
@EnvironmentObject<SettingsModel> private var model
@Default(.countryOfPublicInstances) private var countryOfPublicInstances
@Default(.instances) private var instances
var body: some View {
VStack(alignment: .leading) {
#if os(macOS)
settings
Spacer()
#else
List {
settings
}
#if os(iOS)
.listStyle(.insetGrouped)
#endif
#endif
}
.onAppear(perform: loadCountries)
.onChange(of: countryOfPublicInstances) { newCountry in
InstancesManifest.shared.setPublicAccount(newCountry, accounts: accounts, asCurrent: accounts.current?.isPublic ?? true)
}
.sheet(isPresented: $presentingInstanceForm) {
InstanceForm(savedInstanceID: $savedFormInstanceID)
}
#if os(tvOS)
.frame(maxWidth: 1000)
#endif
.navigationTitle("Locations")
}
@ViewBuilder var settings: some View {
Section(header: SettingsHeader(text: "Public Locations"), footer: countryFooter) {
Picker("Country", selection: $countryOfPublicInstances) {
Text("Don't use public locations").tag(String?.none)
ForEach(countries, id: \.self) { country in
Text(country).tag(Optional(country))
}
}
#if os(tvOS)
.pickerStyle(.inline)
#endif
.disabled(countries.isEmpty)
Button {
InstancesManifest.shared.changePublicAccount(accounts, settings: model)
} label: {
if let account = accounts.current, account.isPublic {
Text("Switch to other public location")
} else {
Text("Switch to public locations")
}
}
.disabled(countryOfPublicInstances.isNil)
}
Section(header: SettingsHeader(text: "Custom Locations")) {
#if os(macOS)
InstancesSettings()
#else
ForEach(instances) { instance in
AccountsNavigationLink(instance: instance)
}
addInstanceButton
#endif
}
}
@ViewBuilder var countryFooter: some View {
if let account = accounts.current {
let locationType = account.isPublic ? (account.country ?? "Unknown") : "Custom"
let description = account.isPublic ? account.url : account.instance?.description ?? "unknown"
Text("Current: \(locationType)\n\(description)")
.foregroundColor(.secondary)
#if os(macOS)
.padding(.bottom, 10)
#endif
}
}
func loadCountries() {
InstancesManifest.shared.instancesList.load()
.onSuccess { response in
if let instances: [ManifestedInstance] = response.typedContent() {
self.countries = instances.map(\.country).unique().sorted()
}
}.onFailure { _ in
model.presentAlert(title: "Could not load locations manifest")
}
}
private var addInstanceButton: some View {
Button {
presentingInstanceForm = true
} label: {
Label("Add Location...", systemImage: "plus")
}
}
}
struct LocationsSettings_Previews: PreviewProvider {
static var previews: some View {
LocationsSettings()
.environmentObject(AccountsModel())
.environmentObject(NavigationModel())
}
}

View File

@@ -26,8 +26,6 @@ struct PlayerSettings: View {
@Default(.enableReturnYouTubeDislike) private var enableReturnYouTubeDislike
@Default(.showMPVPlaybackStats) private var showMPVPlaybackStats
#if os(iOS)
private var idiom: UIUserInterfaceIdiom {
UIDevice.current.userInterfaceIdiom
@@ -103,10 +101,6 @@ struct PlayerSettings: View {
lockOrientationInFullScreenToggle
}
#endif
Section(header: SettingsHeader(text: "Debugging")) {
showMPVPlaybackStatsToggle
}
}
}
@@ -233,10 +227,6 @@ struct PlayerSettings: View {
Toggle("Close PiP and open player when application enters foreground", isOn: $closePiPAndOpenPlayerOnEnteringForeground)
}
#endif
private var showMPVPlaybackStatsToggle: some View {
Toggle("Show MPV playback statistics", isOn: $showMPVPlaybackStats)
}
}
struct PlaybackSettings_Previews: PreviewProvider {

View File

@@ -5,10 +5,10 @@ import SwiftUI
struct SettingsView: View {
#if os(macOS)
private enum Tabs: Hashable {
case instances, browsing, player, history, sponsorBlock, help
case browsing, player, history, sponsorBlock, locations, advanced, help
}
@State private var selection = Tabs.instances
@State private var selection = Tabs.browsing
#endif
@Environment(\.colorScheme) private var colorScheme
@@ -18,24 +18,20 @@ struct SettingsView: View {
#endif
@EnvironmentObject<AccountsModel> private var accounts
@State private var presentingInstanceForm = false
@State private var savedFormInstanceID: Instance.ID?
@EnvironmentObject<NavigationModel> private var navigation
@EnvironmentObject<SettingsModel> private var model
@Default(.instances) private var instances
var body: some View {
settings
.environmentObject(model)
.alert(isPresented: $model.presentingAlert) { model.alert }
}
var settings: some View {
#if os(macOS)
TabView(selection: $selection) {
Form {
InstancesSettings()
.environmentObject(accounts)
}
.tabItem {
Label("Instances", systemImage: "server.rack")
}
.tag(Tabs.instances)
Form {
BrowsingSettings()
}
@@ -68,6 +64,22 @@ struct SettingsView: View {
}
.tag(Tabs.sponsorBlock)
Form {
LocationsSettings()
}
.tabItem {
Label("Locations", systemImage: "globe")
}
.tag(Tabs.locations)
Group {
AdvancedSettings()
}
.tabItem {
Label("Advanced", systemImage: "wrench.and.screwdriver")
}
.tag(Tabs.advanced)
Form {
Help()
}
@@ -88,9 +100,7 @@ struct SettingsView: View {
}
#endif
}
.sheet(isPresented: $presentingInstanceForm) {
InstanceForm(savedInstanceID: $savedFormInstanceID)
}
#endif
}
@@ -99,16 +109,6 @@ struct SettingsView: View {
List {
#if os(tvOS)
AccountSelectionView()
#endif
Section(header: Text("Instances")) {
ForEach(instances) { instance in
AccountsNavigationLink(instance: instance)
}
addInstanceButton
}
#if os(tvOS)
Divider()
#endif
@@ -144,6 +144,18 @@ struct SettingsView: View {
} label: {
Label("SponsorBlock", systemImage: "dollarsign.circle")
}
NavigationLink {
LocationsSettings()
} label: {
Label("Locations", systemImage: "globe")
}
NavigationLink {
AdvancedSettings()
} label: {
Label("Advanced", systemImage: "wrench.and.screwdriver")
}
}
Section(footer: versionString) {
@@ -175,8 +187,6 @@ struct SettingsView: View {
#if os(macOS)
private var windowHeight: Double {
switch selection {
case .instances:
return 390
case .browsing:
return 390
case .player:
@@ -185,6 +195,10 @@ struct SettingsView: View {
return 480
case .sponsorBlock:
return 660
case .locations:
return 480
case .advanced:
return 300
case .help:
return 570
}
@@ -197,14 +211,6 @@ struct SettingsView: View {
.foregroundColor(.secondary)
#endif
}
private var addInstanceButton: some View {
Button {
presentingInstanceForm = true
} label: {
Label("Add Instance...", systemImage: "plus")
}
}
}
struct SettingsView_Previews: PreviewProvider {